BZOJ2120 数颜色(树套树)
B. 数颜色
题目描述
输入格式
输出格式
样例
数据范围与提示
这个玩意拖了一周了…今天把它补上(主要是因为AC自动机卡住了)。
网上用莫队和分块的比较多,然而我都不会……所以就用数据结构写了,其实就是待修改的主席树。
先预处理出 prei 表示位置 i 前第一个和位置 i 颜色相同的位置(map实现),则区间 [L, R] 里有多少不同的数就转化为了求区间 [L, R] 里prei < L 的位置个数,对 pre 数组建主席树(权值线段树),差不多和板子 一样,只是询问要稍微改一下:
1 inline int ask(int u,int v,int a,int b,int l,int r,int k) 2 { 3 if(l==r)return 0; 4 int lm=sum(l(b))+Sum(2,v)-sum(l(a))-Sum(1,u), 5 mid=(l+r)>>1; 6 if(k<=mid) 7 { 8 for(int i=u;i>=1;i-=lowbit(i)) 9 use[1][i]=l(use[1][i]); 10 for(int i=v;i>=1;i-=lowbit(i)) 11 use[2][i]=l(use[2][i]); 12 return ask(u,v,l(a),l(b),l,mid,k); 13 } 14 else 15 { 16 for(int i=u;i>=1;i-=lowbit(i)) 17 use[1][i]=r(use[1][i]); 18 for(int i=v;i>=1;i-=lowbit(i)) 19 use[2][i]=r(use[2][i]); 20 return lm+ask(u,v,r(a),r(b),mid+1,r,k); 21 } 22 }
对于修改,首先想到的是直接重新计算pre数组更新,但是复杂度太大肯定会超时,但是真的每个位置的pre值都会改变吗?其实若将i位置修改,最多改变三个pre:
1.i本身。
2.i之后pre=i的位置,最多一个。
3.i之后pre<i,但是值与更改后数值相等的,最多一个。
这样复杂度就可以接受了。
#include<iostream> #include<cstdio> #include<algorithm> #include<map> using namespace std; struct node { int l,r,sum; #define l(x) tr[x].l #define r(x) tr[x].r #define sum(x) tr[x].sum }tr[10000000]; int cnt,m,T[20000],S[20000],use[3][20000]; int n,Q,a[20000],pre[20000]; map<int,int> mp; inline int read() { int s=0;char a=getchar(); while(a<'0'||a>'9')a=getchar(); while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();} return s; } inline int lowbit(int x){return x&(-x);} inline int build(int l,int r) { int now=++cnt; if(l==r)return now; int mid=(l+r)>>1; l(now)=build(l,mid); r(now)=build(mid+1,r); return now; } inline int build_new(int flag,int mark,int loc,int val) { int rt=++cnt; int before; if(mark==1)before=T[0]; else before=flag?T[mark-1]:S[mark-1]; if(!flag && val)before=S[mark]; sum(rt)=sum(before)+val; int l=1,r=n+1,mid,now=rt,still=before; for(;l(still)||r(still);) { mid=(l+r)>>1; if(loc>mid) { l(now)=l(still),r(now)=++cnt; sum(cnt)=sum(r(still))+val; now=cnt;still=r(still); l=mid+1; } else { r(now)=r(still),l(now)=++cnt; sum(cnt)=sum(l(still))+val; now=cnt;still=l(still); r=mid; } } return rt; } inline int Sum(int y,int x) { int res=0; for(int i=x;i>=1;i-=lowbit(i)) res+=sum(l(use[y][i])); return res; } inline void updata(int loc,int num) { for(int i=loc;i<=n+1;i+=lowbit(i)) S[i]=build_new(0,i,pre[loc],-1); for(int i=loc;i<=n+1;i+=lowbit(i)) S[i]=build_new(0,i,num,1); pre[loc]=num; } inline int ask(int u,int v,int a,int b,int l,int r,int k) { if(l==r)return 0; int lm=sum(l(b))+Sum(2,v)-sum(l(a))-Sum(1,u), mid=(l+r)>>1; if(k<=mid) { for(int i=u;i>=1;i-=lowbit(i)) use[1][i]=l(use[1][i]); for(int i=v;i>=1;i-=lowbit(i)) use[2][i]=l(use[2][i]); return ask(u,v,l(a),l(b),l,mid,k); } else { for(int i=u;i>=1;i-=lowbit(i)) use[1][i]=r(use[1][i]); for(int i=v;i>=1;i-=lowbit(i)) use[2][i]=r(use[2][i]); return lm+ask(u,v,r(a),r(b),mid+1,r,k); } } signed main() { // freopen("in.txt","r",stdin); n=read(),Q=read(); for(int i=1;i<=n;i++) a[i]=read(),pre[i]=mp[a[i]]+1,mp[a[i]]=i; T[0]=build(1,n+1); for(int i=1;i<=n;i++) T[i]=build_new(1,i,pre[i],1); for(int i=1;i<=n;i++) S[i]=build_new(0,i,1,0); char ta;int L,R; for(int i=1;i<=Q;i++) { cin>>ta; L=read(),R=read(); if(ta=='Q') { for(int j=L-1;j>=1;j-=lowbit(j)) use[1][j]=S[j]; for(int j=R;j>=1;j-=lowbit(j)) use[2][j]=S[j]; printf("%ld\n",ask(L-1,R,T[L-1],T[R],1,n+1,L+1)); } else { int tem=pre[L]; updata(L,1); a[L]=R; for(int j=1;j<=n;j++) { if(a[j]==R && j<L) updata(L,j+1); if(pre[j]==L+1) updata(j,tem); if(a[j]==R && j>L && pre[j]-1<L) updata(j,L+1); } } } }
波澜前,面不惊。