题目链接:
题意:给出长度为n的一个数列,每次在线询问区间[L,R]有多少数字出现了偶数次?
思路:分成sqrt(n)块,记录cnt[i][j]表示第i块到第j块的答案数。然后记录每个数字出现的位置。对于询问[L,R],先找到L和R所在的块l和r,若l和r是同一块则直接暴力即可。否则[l+1,r-1]之间的答案已经统计。对于剩下的两段零头,记录这两段零头内出现的数字以及他们的次数,然后查找这些数字在[l+1,r-1]出现的次数,联系在两段零头中出现的次数更新答案。
int pl[420],pr[420],pNum,cnt[420][420];int n,m,K,size;vector<int> V[N];int a[N],c[N]; int Q[N]; int get(int L,int R,int x){ if(L>R) return 0; L=pl[L]; R=pr[R]; int l=0,r=SZ(V[x])-1; while(V[x][l]<L) l++; while(V[x][r]>R) r--; return max(0,r-l+1);} int cal(int L,int R){ int l=0,r=pNum; while(pr[l]<L) l++; while(pl[r]>R) r--; int i,ans=0; if(l==r||l+1==r) { for(i=L;i<=R;i++) { c[a[i]]++; if(c[a[i]]==1) continue; if(c[a[i]]&1) ans--; else ans++; } for(i=L;i<=R;i++) c[a[i]]=0; return ans; } int head=0,tail=0,x,y; for(i=L;i<=pr[l];i++) { c[a[i]]++; if(c[a[i]]==1) Q[tail++]=a[i]; } for(i=pl[r];i<=R;i++) { c[a[i]]++; if(c[a[i]]==1) Q[tail++]=a[i]; } l++; r--; ans=cnt[l][r]; for(i=head;i<tail;i++) { x=Q[i]; y=get(l,r,x); if(y==0) ans+=(c[x]%2==0); else if(y%2==1&&c[x]%2==1) ans++; else if(y%2==0&&c[x]%2==1) ans--; c[x]=0; } return ans;} int main(){ RD(n,m,K); int i,j,k; size=0; while(size*size<n) size++; pNum=0; int L=1,R; while(L<=n) { R=min(n,L+size-1); pl[++pNum]=L; pr[pNum]=R; L=R+1; } pl[0]=-INF; pr[0]=0; pl[++pNum]=n+1; pr[pNum]=INF; FOR1(i,n) RD(a[i]),V[a[i]].pb(i); FOR1(i,m) V[i].pb(n+1); int temp; for(i=1;i<=pNum-1;i++) { FOR1(j,m) c[j]=0; temp=0; for(j=i;j<=pNum-1;j++) { for(k=pl[j];k<=pr[j];k++) { c[a[k]]++; if(c[a[k]]==1) continue; if(c[a[k]]&1) temp--; else temp++; } cnt[i][j]=temp; } } FOR1(i,m) c[i]=0; int ans=0; while(K--) { RD(L,R); L=(L+ans)%n+1; R=(R+ans)%n+1; if(L>R) swap(L,R); ans=cal(L,R); PR(ans); }}