bzoj2821: 作诗(Poetize)
传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2821
思路:分块大法好。。。
分成sqrt(n)块,先预处理出连续的块的答案,f[i][j]表示第i块到第j块的答案。
然后再开一个前缀和数组sum[i][j]表示前i个块第j种字符出现的次数。
对于一组询问[l,r],先得出连续的块的答案。
对于分散的两端的块,暴力考虑每个数对答案的贡献即可。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=100010,maxb=320; using namespace std; int n,c,m,sum[maxb][maxn],f[maxb][maxb],a[maxn],sz,cnt,bel[maxn],l[maxn],r[maxn],tsum[maxn],ans,tmp[maxn]; void work(){ int x,y,L,R;scanf("%d%d",&x,&y); x=(x+ans)%n+1,y=(y+ans)%n+1,ans=0; if (x>y) swap(x,y); //printf("%d %d\n",x,y); L=bel[x],R=bel[y]; if (L==R){ for (int i=x;i<=y;i++) tsum[a[i]]=0; for (int i=x;i<=y;i++) if ((++tsum[a[i]])!=1) ans+=(tsum[a[i]]&1)?-1:1; } else{ ans=f[L+1][R-1]; //printf("ans%d %d %d\n",L,R,ans); for (int i=x;i<=r[L];i++) tsum[a[i]]=sum[R-1][a[i]]-sum[L][a[i]]; for (int i=l[R];i<=y;i++) tsum[a[i]]=sum[R-1][a[i]]-sum[L][a[i]]; //for (int i=1;i<=c;i++) printf("tsum%d\n",tsum[i]); for (int i=x;i<=r[L];i++) if (++tsum[a[i]]!=1) ans+=(tsum[a[i]]&1)?-1:1; for (int i=l[R];i<=y;i++) if (++tsum[a[i]]!=1) ans+=(tsum[a[i]]&1)?-1:1; } printf("%d\n",ans); } int main(){ scanf("%d%d%d",&n,&c,&m),sz=sqrt(n),cnt=n/sz+(n%sz!=0); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) bel[i]=(i-1)/sz+1; for (int i=1;i<=n;i++){r[bel[i]]=i;if (!l[bel[i]]) l[bel[i]]=i;} for (int i=1;i<=cnt;i++){ for (int j=1;j<=c;j++) sum[i][j]=sum[i-1][j]; for (int j=l[i];j<=r[i];j++) sum[i][a[j]]++; } for (int i=1;i<=cnt;i++){ for (int j=1;j<=c;j++) tsum[j]=0;int tmp=0; for (int j=i;j<=cnt;j++){ for (int k=l[j];k<=r[j];k++) if (tsum[a[k]]++) tmp+=(tsum[a[k]]&1)?-1:1; f[i][j]=tmp; } } //for (int i=1;i<=cnt;i++) printf("%d %d\n",l[i],r[i]); /*for (int i=1;i<=cnt;i++,puts("")) for (int j=1;j<=cnt;j++) printf("%d ",f[i][j]);*/ for (int i=1;i<=m;i++) work(); return 0; }