[分块] Jzoj P5924 Queue
题解
- 可以考虑分块,只用维护块内每个颜色出现的次数
- 轮转的话,就将被包含的块的最后一个数放到下一个块的块首,再将最后一个数往前放
- 查询的话,整块直接求,散块暴力求
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cmath> 4 #define N 200010 5 using namespace std; 6 int n,m,num,l,a[N],pre[N],next[N],cnt[450][N],k[N],d[505],last[505]; 7 int find(int x) 8 { 9 int y=last[k[x]],w=min(n,num*k[x]); 10 while (w>x) y=pre[y],w--; 11 return y; 12 } 13 void del(int x,int y) 14 { 15 if (!x) return; 16 if (pre[x]) next[pre[x]]=next[x]; 17 if (next[x]) pre[next[x]]=pre[x]; 18 cnt[y][a[x]]--; 19 if (!pre[x]) d[y]=next[x]; 20 if (!next[x]) last[y]=pre[x]; 21 } 22 void add(int x,int y,int z) 23 { 24 if (!x) return; 25 next[pre[x]=pre[y]]=x,pre[next[x]=y]=x; 26 pre[0]=next[0]=0,cnt[z][a[x]]++; 27 if (!pre[x]) d[z]=x; 28 if (!next[x]) last[z]=x; 29 } 30 int main() 31 { 32 freopen("queue.in","r",stdin),freopen("queue.out","w",stdout); 33 scanf("%d%d",&n,&m); 34 if (n==0) return 0; 35 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 36 num=n/sqrt(n),k[0]=1; 37 for (int i=1;i<=n;i++) 38 { 39 pre[i]=i-1,next[i]=i+1,l++; 40 if (l>num) 41 { 42 l=1; 43 k[i]=k[i-1]+1,last[k[i-1]]=i-1; 44 d[k[i]]=i,pre[i]=next[i-1]=0; 45 } 46 else k[i]=k[i-1]; 47 cnt[k[i]][a[i]]++; 48 } 49 next[n]=0,last[k[n]]=n,d[1]=1; 50 for (int i=1,l,r,op,z;i<=m;i++) 51 { 52 scanf("%d%d%d",&op,&l,&r); 53 if (l==r) continue; 54 if (op==1) 55 { 56 int x=find(l),y=find(r); 57 del(y,k[r]),add(y,x,k[l]); 58 for (int j=k[l];j<=k[r]-1;j++) 59 { 60 int p=last[j]; 61 del(p,j),add(p,d[j+1],j+1); 62 } 63 } 64 else 65 { 66 scanf("%d",&z); 67 int x=find(l),y=find(r),ans=0; 68 while (x&&x!=y) ans+=(a[x]==z),x=next[x]; 69 while (y&&x!=y) ans+=(a[y]==z),y=pre[y]; 70 if (x==y&&x!=0) ans+=(a[x]==z); else for (int j=k[l]+1;j<=k[r]-1;j++) ans+=cnt[j][z]; 71 printf("%d\n",ans); 72 } 73 } 74 75 }