[BZOJ2821]作诗(分块)

题意

N个数,M组询问,每次问[l,r]中有多少个数出现正偶数次
对于100%的数据,1≤n,c,m≤105

题解

(传说lyd省选的时候看错题   把题看成这个了   从此又多了一道分块神题)
把N个数分成sqrt(n)块,预处理d[i][j]表示第i块起点到第j块末尾的答案  枚举起点i,并维护一个数组记录每个数到目前为止出现的次数,从偶变奇、从奇变偶时相应增减答案。  把每个数在数列中出现的位置从小到大排序后放入到一个数组Arr中备用。  读入每个询问[l,r]。如果l和r在同一个块中暴力即可,否则设l所在块的末尾为l’,r所在块的起点为r’,[l’+1,r’-1]的答案已经预处理出。扫描l~l’, r’~r的所有数,统计每个数出现的次数cnt,第一次出现时把它加入队列。  对于队列中的每个数,在数组Arr中二分l’+1和r’-1,得到在[l’+1,r’-1]中出现的次数k。通过对k和当前队列中的数的cnt进行奇偶性讨论更新答案

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<vector>
  7 using namespace std;
  8 const int N=100100;
  9 vector<int> vec[N];
 10 int n,c,m,a[N],block[N],Block,R[1600],L[1600],cnt[N],f[1600][1600],ans,stack[N],top;
 11 inline int read(){
 12     int f=1;int x=0;char ch=getchar();
 13     while(ch<'0'||ch>'9'){
 14         if(ch=='-')f=-1;
 15         ch=getchar();
 16     }
 17     while(ch>='0'&&ch<='9'){
 18         x=(x<<3)+(x<<1)+ch-'0';
 19         ch=getchar();
 20     }
 21     return x*f;
 22 }
 23 int find1(int x,int y){
 24     int l=0;int r=vec[x].size()-1;
 25     int tmp=-99999;
 26     while(l<=r){
 27         int mid=(l+r)>>1;
 28         if(vec[x][mid]<=y){
 29             tmp=mid;
 30             l=mid+1;
 31         }
 32         else r=mid-1;
 33     }
 34     return tmp;
 35 }
 36 int find2(int x,int y){
 37     int l=0;int r=vec[x].size()-1;
 38     int tmp=9999;
 39     while(l<=r){
 40         int mid=(l+r)>>1;
 41         if(vec[x][mid]>=y){
 42             tmp=mid;
 43             r=mid-1;
 44         }
 45         else l=mid+1;
 46     }
 47     return tmp;
 48 }
 49 int main(){
 50     n=read();c=read();m=read();
 51     Block=sqrt(n);
 52     for(int i=1;i<=n;i++){
 53         a[i]=read();
 54         vec[a[i]].push_back(i);
 55         block[i]=(i-1)/Block+1;
 56         if(!L[block[i]])L[block[i]]=i;
 57         R[block[i]]=i;
 58     }
 59     for(int i=1;i<=block[n];i++){    
 60         int tot=0;
 61         for(int j=L[i];j<=n;j++){
 62             if(!(cnt[a[j]]&1)&&cnt[a[j]])tot--;
 63             cnt[a[j]]++;
 64             if(!(cnt[a[j]]&1))tot++;
 65             f[i][block[j]]=tot;
 66         } 
 67         for(int j=L[i];j<=n;j++){
 68             cnt[a[j]]=0;
 69         }
 70     }
 71     while(m--){
 72         int l,r;
 73         l=read();r=read();
 74         l=(l+ans)%n+1;r=(r+ans)%n+1;
 75         if(l>r)swap(l,r);
 76         if(block[l]+1>=block[r]){
 77             ans=0;
 78             for(int i=l;i<=r;i++){
 79                 cnt[a[i]]++;
 80                 if(cnt[a[i]]==1)continue;
 81                 if(cnt[a[i]]&1)ans--;
 82                 else ans++;
 83             } 
 84             for(int i=l;i<=r;i++)
 85                 cnt[a[i]]=0;
 86         } 
 87         else{
 88             ans=f[block[l]+1][block[r]-1];
 89             top=0;
 90             for(int i=l;i<=R[block[l]];i++){
 91                 cnt[a[i]]++;
 92                 if(cnt[a[i]]==1)stack[++top]=a[i];
 93             } 
 94             for(int i=L[block[r]];i<=r;i++){
 95                 cnt[a[i]]++;
 96                 if(cnt[a[i]]==1)stack[++top]=a[i];
 97             } 
 98             while(top){
 99                 int z=stack[top--];
100                 int tmp=max(find1(z,L[block[r]]-1)-find2(z,R[block[l]]+1)+1,0);
101                 if(tmp==0){
102                     if(cnt[z]%2==0)ans++; 
103                 }
104                 else{
105                     if(tmp&1){if(cnt[z]&1)ans++;}
106                     else {if(cnt[z]&1)ans--;}
107                 }
108                 cnt[z]=0;
109             }
110         }
111         printf("%d\n",ans);
112     }
113     return 0;
114 } 

 

posted @ 2018-08-14 20:29  Xu-daxia  阅读(152)  评论(0编辑  收藏  举报