1.AT_dp_w Intervals
我的博客
2.CF377D Developing Games
我的博客
这两道题是比较经典的线段树区间 trick,希望自己可以在以后的比赛中手切。
3.洛谷 P10814 离线二维数点
题意
给你一个长为 n n n 的序列 a a a,有 m m m 次询问,每次询问给定 l , r , x l,r,x l,r,x,求 [ l , r ] [l,r] [l,r] 区间中小于等于 x x x 的元素个数。
1 ≤ n , m , a i , l , r , x ≤ 2 × 1 0 6 1\le n,m,a_i,l,r,x\le 2\times10^6 1≤n,m,ai,l,r,x≤2×106。
思路
前缀和思想,用 [ 1 , r ] [1,r] [1,r] 中小于等于 x x x 的数量,减去 [ 1 , l − 1 ] [1,l-1] [1,l−1] 中小于等于 x x x 的数量,就可以了。
考虑枚举条件端点,从而改变权值。
用线段树常数比较大,在洛谷 TLE 最后一个点,还是用树状数组吧,反正也比较方便。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read()
{ll s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;
}
inline void write(ll x)
{ if(x==0){putchar('0');return;}ll len=0,k1=x,c[10005];if(k1<0)k1=-k1,putchar('-');while(k1)c[len++]=k1%10+'0',k1/=10;while(len--)putchar(c[len]);
}
#define ls u<<1
#define rs u<<1|1
const ll N=2e6+9;
ll n,m,a[N];
ll M,ans[N];
struct term
{ll d,id,v;
};
vector<term>p[N];
struct BT
{ll T[N];ll lowbit(ll x){return x&(-x);}void add(ll x,ll k){for(int i=x;i<=M;i+=lowbit(i))T[i]+=k;}ll query(ll x){ll ret=0;for(int i=x;i>=1;i-=lowbit(i))ret+=T[i];return ret;}
}B;
int main()
{n=read(),m=read();for(int i=1;i<=n;i++){a[i]=read();M=max(M,a[i]);}for(int i=1;i<=m;i++){ll l,r,d;l=read(),r=read(),d=read();p[l-1].push_back((term){d,i,-1});p[r].push_back((term){d,i,1});M=max(M,d);}for(int i=1;i<=n;i++){B.add(a[i],1);for(auto x:p[i])ans[x.id]+=x.v*B.query(x.d);}for(int i=1;i<=m;i++)write(ans[i]),puts("");return 0;
}
4.洛谷 P1972 SDOI2009 HH的项链
给定一个序列 a i a_i ai, m m m 次询问区间 [ l , r ] [l,r] [l,r] 中有多少种不同的数。
1 ≤ n , m , ∀ a i ≤ 1 0 6 1\le n,m,\forall a_i\le 10^6 1≤n,m,∀ai≤106, 1 ≤ l ≤ r ≤ n 1\le l\le r\le n 1≤l≤r≤n。
思路
一眼想用莫队,但是 Θ ( n n ) \Theta(n\sqrt{n}) Θ(nn),令人畏惧(在古早时期学莫队的时候卡常过了),那么分块也并非正解了。
区间内有不同的数,那么多次出现的只算做一个。这题还是查询区间,那么能不能用二维数点来做呢?
设序列中 a i a_i ai 上一次出现在下标 p r e i pre_i prei,没有则 p r e i = 0 pre_i=0 prei=0,如果一个数在下标区间 [ l , r ] [l,r] [l,r] 中首次出现,那么 p o s i < l pos_i<l posi<l;如果出现第二次,那么必然 p r e i ∈ [ l , r ] pre_i\in[l,r] prei∈[l,r]。
那么题目就转化成了,求区间 [ l , r ] [l,r] [l,r] 中,有多少个 p r e i < l pre_i<l prei<l。
那不就和上一题一样了吗?同样使用前缀和思想,枚举右端点。
不过这一题有 0 0 0 出现,扔上树状数组会出问题,所以记得加 1 1 1。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e6+9;
ll n,m,a[N];
ll pre[N],pos[N];
ll ans[N];
struct que
{ll l,r,id;
}q[N];
bool cmp(que x,que y)
{if(x.l!=y.l)return x.l<y.l;return x.r<y.r;
}
struct term
{ll d,id,v;
};
vector<term>p[N];
struct BT
{ll T[N];ll lowbit(ll x){return x&(-x);}void add(ll x,ll k){for(int i=x;i<=n;i+=lowbit(i))T[i]+=k;}ll query(ll x){ll ret=0;for(int i=x;i>=1;i-=lowbit(i))ret+=T[i];return ret;}
}B;
int main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);pre[i]=pos[a[i]];pos[a[i]]=i;}scanf("%lld",&m);for(int i=1;i<=m;i++){ll l,r;scanf("%lld%lld",&l,&r);q[i]=(que){l,r,i};p[l-1].push_back((term){l-1,i,-1});p[r].push_back((term){l-1,i,1});}sort(q+1,q+m+1,cmp);//答案就是[l,r]中pre(i)<l的个数 for(int i=1;i<=n;i++){B.add(pre[i]+1,1);for(auto x:p[i])ans[x.id]+=x.v*B.query(x.d+1);}for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);return 0;
}