P4135 作诗
• 很容易想到分块。
• 类似于区间众数的操作。
• 首先预处理出两个数组:
1. f 二维数组表示两个块为左右边界的序列的答案。
2. s 二维数组处理出从第一个块到某一个块之间每
个数出现的次数。
• 对于询问其实和预处理 f 数组的做法差不多。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int l,r; 4 int n,m,c,block,sm; 5 int val[100050]; 6 int f[350][350]; 7 int col[100050]; 8 int L[350],R[350]; 9 int s[350][100050]; 10 int t[100050],ans; 11 int T[100050],TOP; 12 void calc() 13 { 14 ans=f[col[l]+1][col[r]-1]; TOP=0; 15 if(col[l]!=col[r]) 16 { 17 for(int i=l;i<=R[col[l]];++i) 18 T[++TOP]=val[i]; 19 for(int i=r;i>=L[col[r]];--i) 20 T[++TOP]=val[i]; 21 for(int i=1;i<=TOP;++i) 22 t[T[i]]=s[col[r]-1][T[i]]-s[col[l]][T[i]]; 23 } 24 else 25 { 26 for(int i=l;i<=r;++i) 27 T[++TOP]=val[i]; 28 for(int i=1;i<=TOP;++i) 29 t[T[i]]=0; 30 } 31 } 32 int main() 33 { 34 scanf("%d%d%d",&n,&c,&m); 35 block=sqrt(n);sm=n/block+1; 36 for(int i=1;i<=sm;++i) 37 L[i]=n,R[i]=1; 38 for(int i=1;i<=n;++i) 39 { 40 col[i]=i/block+1; 41 scanf("%d",&val[i]); 42 ++s[col[i]][val[i]]; 43 L[col[i]]=min(L[col[i]],i); 44 R[col[i]]=max(R[col[i]],i); 45 } 46 for(int i=1;i<=sm;++i) 47 for(int j=1;j<=c;++j) 48 s[i][j]+=s[i-1][j]; 49 for(int i=1;i<=sm;++i) 50 { 51 for(int j=L[i];j<=n;++j) 52 { 53 ++t[val[j]]; 54 if(t[val[j]]==1) 55 ; 56 else if(t[val[j]]&1) 57 --ans; 58 else 59 ++ans; 60 if(j==R[col[j]]) 61 f[i][col[j]]=ans; 62 } 63 memset(t,0,sizeof(t)); 64 ans=0; 65 } 66 while(m--) 67 { 68 scanf("%d%d",&l,&r); 69 l=(l+ans)%n+1; 70 r=(r+ans)%n+1; 71 if(l>r) 72 swap(l,r); 73 calc(); 74 for(int i=1;i<=TOP;++i) 75 { 76 ++t[T[i]]; 77 if(t[T[i]]==1) 78 ; 79 else if(t[T[i]]&1) 80 --ans; 81 else 82 ++ans; 83 } 84 printf("%d\n",ans); 85 } 86 return 0; 87 }