感觉我直到前两天才算是真正学会用树状数组啊囧。。
有一类离线的区间询问问题,可以有以下解法(我暂时会这么多):离线+树状数组||线段树||莫队算法
线段树先不说了,好久没写过了,而且本文的标题是树状数组与莫队囧。。
用树状数组的关键在于,每次插入一个点或者删除一个点要维护什么东西,一般是插入右端点删除左端点,维护的时候也要注意想清楚对哪部分答案有影响,删除之后要及时处理。
而莫队的不同就在于,离线的时候是分块处理询问的,然后每次只需要维护当前区间内的信息,大多数情况下需要维护的东西都比树状数组等少很多,因此想起来和写起来都非常舒服,这种情况下增加一个端点和删除一个端点要做的事情也不会很多。不过要注意的一点就是对于10w级别的数据,这里的增加和删除操作必须是O(1)才不会超时。
还是随便贴一下代码吧:
hdu4358
莫队
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<string> #include<iomanip> #pragma comment(linker, "/STACK:1024000000,1024000000") #include<vector> #include<set> #include<map> #include<queue> #include<list> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define rep(i,k,n) for(int i=(k);i<=(n);i++) #define red(i,k,n) for(int i=(k);i>=(n);i--) #define sqr(x) ((x)*(x)) #define clr(x,y) memset((x),(y),sizeof(x)) #define mod 1000000007 #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)>(b)?(b):(a)) const int MAXN = 100010; int aa[MAXN],a[MAXN],l[MAXN],r[MAXN],p[MAXN]; int n,k,m,dfn,cnt; struct Q { int l,r,id,pos; bool operator < (const Q &a){return pos==a.pos?r<a.r:pos<a.pos;} }q[MAXN]; int ans[MAXN]; vector<int> e[MAXN]; bool vis[MAXN]; void dfs(int u) { l[u]=++dfn; a[dfn]=aa[u]; vis[u]=1; rep(i,0,e[u].size()-1) { if(!vis[e[u][i]]) { dfs(e[u][i]); } } r[u]=dfn; } bool cmp(int x,int y){return aa[x]<aa[y];} int d[MAXN]; void dis() { rep(i,1,n)d[i]=i; sort(d+1,d+1+n,cmp); int pre=aa[d[1]]; aa[d[1]]=1; int acnt=1; rep(i,2,n) { if(aa[d[i]]==pre)aa[d[i]]=acnt; else {pre=aa[d[i]];aa[d[i]]=++acnt;} } } void add(int x) { if(p[a[x]]==k-1)cnt++; if(p[a[x]]==k)cnt--; p[a[x]]++; } void del(int x) { if(p[a[x]]==k+1)cnt++; if(p[a[x]]==k)cnt--; p[a[x]]--; } int main() { //#define LOCAL #ifdef LOCAL freopen("e:\\read.txt","r",stdin); //freopen("e:\\write.txt","w",stdout); #endif int T; cin>>T; rep(ii,1,T) { scanf("%d%d",&n,&k); int gn=sqrt(n*1.0); rep(i,1,n){scanf("%d",&aa[i]);e[i].clear();} dis(); rep(i,1,n-1) { int u,v; scanf("%d%d",&u,&v); e[u].push_back(v); e[v].push_back(u); } dfn=0; clr(vis,0); dfs(1); scanf("%d",&m); rep(i,1,m) { int qq; scanf("%d",&qq); q[i].l=l[qq]; q[i].r=r[qq]; q[i].id=i; q[i].pos=q[i].l/gn; } sort(q+1,q+1+m); clr(p,0); int ll=1,rr=0; cnt=0; rep(i,1,m) { while(ll<q[i].l)del(ll++); while(ll>q[i].l)add(--ll); while(rr>q[i].r)del(rr--); while(rr<q[i].r)add(++rr); ans[q[i].id]=cnt; } printf("Case #%d:\n",ii); rep(i,1,m)printf("%d\n",ans[i]); if(ii<T)puts(""); } return 0; }
树状数组
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<string> #include<iomanip> #pragma comment(linker, "/STACK:1024000000,1024000000") #include<vector> #include<set> #include<map> #include<queue> #include<list> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define rep(i,k,n) for(int i=(k);i<=(n);i++) #define red(i,k,n) for(int i=(k);i>=(n);i--) #define sqr(x) ((x)*(x)) #define clr(x,y) memset((x),(y),sizeof(x)) #define mod 1000000007 #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)>(b)?(b):(a)) const int MAXN = 100010; int aa[MAXN],a[MAXN],l[MAXN],r[MAXN],nxt[MAXN]; int n,k,m,dfn,acnt; struct Q { int l,r,id; bool operator < (const Q &a){return r<a.r;} }q[MAXN]; int ans[MAXN]; struct Bit { int bit[MAXN]; void cl(){clr(bit,0);} void add(int k,int v) { for(int i=k;i>0;i-=i&-i)bit[i]+=v; } int sum(int k) { int ret=0; for(int i=k;i<=n;i+=i&-i)ret+=bit[i]; return ret; } }B; map<int,int> mp,L,R; vector<int> e[MAXN]; bool vis[MAXN]; void dfs(int u) { l[u]=++dfn; a[dfn]=aa[u]; vis[u]=1; rep(i,0,e[u].size()-1) { if(!vis[e[u][i]]) { dfs(e[u][i]); } } r[u]=dfn; } int main() { //#define LOCAL #ifdef LOCAL freopen("e:\\read.txt","r",stdin); //freopen("e:\\write.txt","w",stdout); #endif int T; cin>>T; rep(ii,1,T) { B.cl(); scanf("%d%d",&n,&k); rep(i,1,n){scanf("%d",&aa[i]);e[i].clear();} mp.clear(); rep(i,1,n-1) { int u,v; scanf("%d%d",&u,&v); e[u].push_back(v); e[v].push_back(u); } dfn=0; clr(vis,0); dfs(1); scanf("%d",&m); rep(i,1,m) { int qq; scanf("%d",&qq); q[i].l=l[qq]; q[i].r=r[qq]; q[i].id=i; } sort(q+1,q+1+m); int t=1; L.clear(); R.clear(); clr(nxt,0); rep(i,1,n) { mp[a[i]]++; if(L[a[i]]==0) { L[a[i]]=i; } else { nxt[R[a[i]]]=i; } if(mp[a[i]]==k) { B.add(L[a[i]],1); } else if(mp[a[i]]>k) { B.add(L[a[i]],-2); L[a[i]]=nxt[L[a[i]]]; B.add(L[a[i]],1); } R[a[i]]=i; while(t<=m&&i==q[t].r){ans[q[t].id]=B.sum(q[t].l);t++;} if(t>m)break; } printf("Case #%d:\n",ii); rep(i,1,m)printf("%d\n",ans[i]); if(ii<T)puts(""); } return 0; }
用莫队的话很多题都变成水题了。。。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<string> #include<iomanip> #pragma comment(linker, "/STACK:1024000000,1024000000") #include<vector> #include<set> #include<map> #include<queue> #include<list> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define rep(i,k,n) for(int i=(k);i<=(n);i++) #define red(i,k,n) for(int i=(k);i>=(n);i--) #define sqr(x) ((x)*(x)) #define clr(x,y) memset((x),(y),sizeof(x)) #define mod 1000000007 #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)>(b)?(b):(a)) const int MAXN = 100010; int n,m,a[MAXN],ku,cnt; bool in[MAXN]; struct Q { int l,r,id,pos; bool operator < (const Q &a){return pos==a.pos?r<a.r:pos<a.pos;} void read(int d){scanf("%d%d",&l,&r);id=d;pos=l/ku;} }q[MAXN]; int ans[MAXN]; void add(int x) { if(in[x-1]&&in[x+1])cnt--; else if(!in[x-1]&&!in[x+1])cnt++; in[x]=true; } void del(int x) { if(in[x-1]&&in[x+1])cnt++; else if(!in[x-1]&&!in[x+1])cnt--; in[x]=false; } int main() { //#define LOCAL #ifdef LOCAL freopen("e:\\read.txt","r",stdin); //freopen("e:\\write.txt","w",stdout); #endif int T; cin>>T; while(T--) { scanf("%d%d",&n,&m); rep(i,1,n)scanf("%d",&a[i]); ku=sqrt(n*1.0); rep(i,1,m)q[i].read(i); sort(q+1,q+1+m); int l=1,r=0; cnt=0; clr(in,false); rep(i,1,m) { while(r<q[i].r){r++;add(a[r]);} while(r>q[i].r){del(a[r]);r--;} while(l<q[i].l){del(a[l]);l++;} while(l>q[i].l){l--;add(a[l]);} ans[q[i].id]=cnt; } rep(i,1,m)printf("%d\n",ans[i]); } return 0; }