【BZOJ2821】作诗(Poetize)—(分块)
还算好的一道分块题了吧
除了细节问题炸锅1h++以外
考虑要询问出现偶数次的数的个数
我们可以预处理出两个数组:
表示这个数在前块中出现了多少次
这样我们就可以求出一个数在第~块出现的次数
表示第块到第块内出现了偶数次的数的个数
这样对于每次询问,我们直接得到所有整块内的答案
然后再对两边长为的区间统计一下每个数出现的次数,再和整块内的数的出现次数分类讨论一下得到答案
复杂度
其实由均值法分析的最适合的块的大小是
但空间开不下就只能了
注意细节!!
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
int res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res;
}
const int N=100005;
const int M=318;
int n,c,buc[N],plc[N],m,cnt,blo,l[M],ans,r[M],num[M][N],a[N],val[M][M];
int main(){
n=read(),c=read(),m=read();
for(int i=1;i<=n;++i)a[i]=read();
blo=sqrt(n),cnt=(n-1)/blo+1;
for(int i=1;i<=n;i++)plc[i]=(i-1)/blo+1;
for(int i=1;i<=cnt;i++)l[i]=(i-1)*blo+1,r[i]=min(i*blo,n);
for(int i=1;i<=n;i++){
for(int j=plc[i];j<=cnt;j++){
num[j][a[i]]++;
}
}
for(int i=1;i<=cnt;i++){
int tmp=0;
for(int j=l[i];j<=n;j++){
if(buc[a[j]]&1)tmp++;
else if(buc[a[j]]>0)tmp--;
buc[a[j]]++;
if(j==r[plc[j]])val[i][plc[j]]=tmp;
}
memset(buc,0,sizeof(buc));
}
while(m--){
int x=(read()+ans)%n+1,y=(read()+ans)%n+1;
if(x>y) swap(x,y);ans=0;
if(plc[x]==plc[y]||plc[x]+1==plc[y]){
for(int i=x;i<=y;i++)buc[a[i]]++;
for(int i=x;i<=y;i++){
if(buc[a[i]]==0)continue;
if(!(buc[a[i]]&1))ans++;
buc[a[i]]=0;
}
}
else{
ans=val[plc[x]+1][plc[y]-1];
for(int i=x;i<=r[plc[x]];i++)buc[a[i]]++;
for(int i=l[plc[y]];i<=y;i++)buc[a[i]]++;//先全部加进桶里
for(int i=x;i<=r[plc[x]];i++){
if(buc[a[i]]==0)continue;
int tmp=num[plc[y]-1][a[i]]-num[plc[x]][a[i]];
if(tmp==0){//注意这里if语句的顺序
if(!(buc[a[i]]&1))ans++;
}
else if((tmp&1)&&(buc[a[i]]&1))ans++;
else if((!(tmp&1))&&(buc[a[i]]&1))ans--;
buc[a[i]]=0;
}
for(int i=l[plc[y]];i<=y;i++){
if(buc[a[i]]==0)continue;
int tmp=num[plc[y]-1][a[i]]-num[plc[x]][a[i]];
if(tmp==0){//注意这里if语句的顺序
if(!(buc[a[i]]&1))ans++;
}
else if((tmp&1)&&(buc[a[i]]&1))ans++;
else if((!(tmp&1))&&(buc[a[i]]&1))ans--;
buc[a[i]]=0;
}
}
cout<<ans<<'\n';
}
}