BZOJ2453:维护队列——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=2453
Description
你小时候玩过弹珠吗?
小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色。但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你来寻求帮助。
Input
输入文件第一行包含两个整数N和M。
第二行N个整数,表示初始队列中弹珠的颜色。
接下来M行,每行的形式为“Q L R”或“R x c”,“Q L R”表示A想知道从队列第L个弹珠到第R个弹珠中,一共有多少不同颜色的弹珠,“R x c”表示A把x位置上的弹珠换成了c颜色。
Output
对于每个Q操作,输出一行表示询问结果。
Sample Input
2 3
1 2
Q 1 2
R 1 2
Q 1 2
1 2
Q 1 2
R 1 2
Q 1 2
Sample Output
2
1
1
HINT
对于100%的数据,有1 ≤ N ≤ 10000, 1 ≤ M ≤ 10000,小朋友A不会修改超过1000次,所有颜色均用1到10^6的整数表示。
——————————————————————————————————————
一种单点修改的分块。
我们维护:
1.sum[i][j]:前j块i元素出现次数。
2.ans[i][j]:i~j块的颜色种数。
修改的时候我们显然sum数组可以O(sqrtN)修改成功,而ans数组通过暴力也可以在O(N/4)的复杂度中修改成功。
(实现方法看代码吧……)
剩下的查询就比较简单了(可能是分块分多的错觉)
#include<cstdio> #include<queue> #include<cctype> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int N=10010; const int SQRTN=110; const int M=11010; const int INF=2147483647; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch=='-';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct question{ char op[10]; int l,r; }q[N]; int n,m,lim,s,cnt,a[N],b[M],bl[SQRTN],br[SQRTN]; int sum[M][SQRTN],ans[SQRTN][SQRTN]; bool vis[M]; inline void initLSH(){ sort(b+1,b+lim+1); lim=unique(b+1,b+lim+1)-b-1; for(int i=1;i<=n;i++){ a[i]=lower_bound(b+1,b+lim+1,a[i])-b; } return; } inline int LSH(int x){return lower_bound(b+1,b+lim+1,x)-b;} inline void intoblock(){ for(int i=1;i<=n;i++){ if(i%s==1){br[cnt]=i-1;bl[++cnt]=i;} } br[cnt]=n;bl[cnt+1]=n+1; return; } inline void init(){ for(int i=1;i<=cnt;i++){ for(int j=1;j<=lim;j++)sum[j][i]=sum[j][i-1]; for(int j=bl[i];j<=br[i];j++){ sum[a[j]][i]++; } } for(int i=1;i<=cnt;i++){ for(int j=i;j<=cnt;j++){ ans[i][j]=ans[i][j-1]; for(int k=bl[j];k<=br[j];k++)vis[a[k]]=0; for(int k=bl[j];k<=br[j];k++){ int t=a[k]; if(!vis[t]){ int c=sum[t][j-1]-sum[t][i-1]; if(!c)ans[i][j]++; vis[t]=1; } } } } return; } inline void modify(int x,int c){ if(a[x]==c)return; int now=(x-1)/s+1; int old=a[x]; a[x]=c; int nw=a[x]; for(int i=1;i<=now;i++){ for(int j=now;j<=cnt;j++){ if(sum[old][j]-sum[old][i-1]==1)ans[i][j]--; if(sum[nw][j]-sum[nw][i-1]==0)ans[i][j]++; } } for(int i=now;i<=cnt;i++)sum[old][i]--,sum[nw][i]++; return; } inline int query(int l,int r){ int cur=0; if(r-l+1<=2*s){ for(int i=l;i<=r;i++)vis[a[i]]=0; for(int i=l;i<=r;i++){ int t=a[i]; if(!vis[t]){ cur++; vis[t]=1; } } return cur; } int L=(l-1)/s+1,R=(r-1)/s+1; cur+=ans[L+1][R-1]; for(int i=l;i<=br[L];i++)vis[a[i]]=0; for(int i=bl[R];i<=r;i++)vis[a[i]]=0; for(int i=l;i<=br[L];i++){ if(!vis[a[i]]){ int c=sum[a[i]][R-1]-sum[a[i]][L]; if(!c)cur++; vis[a[i]]=1; } } for(int i=bl[R];i<=r;i++){ if(!vis[a[i]]){ int c=sum[a[i]][R-1]-sum[a[i]][L]; if(!c)cur++; vis[a[i]]=1; } } return cur; } int main(){ n=read();m=read();s=sqrt(n); for(int i=1;i<=n;i++)a[i]=b[++lim]=read(); for(int i=1;i<=m;i++){ cin>>q[i].op; q[i].l=read(),q[i].r=read(); if(q[i].op[0]=='R')b[++lim]=q[i].r; } initLSH(); intoblock(); init(); for(int i=1;i<=m;i++){ if(q[i].op[0]=='R')modify(q[i].l,LSH(q[i].r)); else printf("%d\n",query(q[i].l,q[i].r)); } return 0; }