BZOJ 2821: 作诗(Poetize)(分块)
题面
Time Limit: 50 Sec Memory Limit: 128 MB
Submit: 3596 Solved: 1070
[Submit][Status][Discuss]
Description
神犇SJY虐完HEOI之后给傻×LYD出了一题:SHY是T国的公主,平时的一大爱好是作诗。由于时间紧迫,SHY作完诗
之后还要虐OI,于是SHY找来一篇长度为N的文章,阅读M次,每次只阅读其中连续的一段[l,r],从这一段中选出一
些汉字构成诗。因为SHY喜欢对偶,所以SHY规定最后选出的每个汉字都必须在[l,r]里出现了正偶数次。而且SHY认
为选出的汉字的种类数(两个一样的汉字称为同一种)越多越好(为了拿到更多的素材!)。于是SHY请LYD安排选
法。LYD这种傻×当然不会了,于是向你请教……问题简述:N个数,M组询问,每次问[l,r]中有多少个数出现正偶
数次。
Input
输入第一行三个整数n、c以及m。表示文章字数、汉字的种类数、要选择M次。第二行有n个整数,每个数Ai在[1, c
]间,代表一个编码为Ai的汉字。接下来m行每行两个整数l和r,设上一个询问的答案为ans(第一个询问时ans=0),
令L=(l+ans)mod n+1, R=(r+ans)mod n+1,若L>R,交换L和R,则本次询问为[L,R]。
Output
输出共m行,每行一个整数,第i个数表示SHY第i次能选出的汉字的最多种类数。
Sample Input
5 3 5
1 2 2 3 1
0 4
1 2
2 2
2 3
3 5
Sample Output
2
0
0
0
1
HINT
对于100%的数据,1<=n,c,m<=10^5
解题思路
区间出现偶数次的个数,强制在线无法莫队。那只好分块了,刚开始写了个\(n^{5/3}\)的丑陋分块,直接\(T\)飞。后来%了%题解发现自己预处理写丑了,其实可以\(nsqrt(n)\),预处理倒序枚举块,然后在从块的右端枚举点。算出\(cnt[i][j]\)表示\(i\)这个数字在前\(j\)块的出现次数,\(ans[i][j]\)表示\(i,j\)两块的答案。询问时整块直接调用\(ans\),边角块暴力枚举修改答案,时间复杂度O(nsqrt(n))
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN = 100005;
inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return f?x:-x;
}
int n,c,m,a[MAXN],siz,cnt[MAXN][320],num,Ans;
int bl[MAXN],l[MAXN],r[MAXN],ans[320][320],tmp[MAXN];
int query(int x,int y){
if(bl[x]==bl[y]){
int ret=0;
for(int i=x;i<=y;i++){
tmp[a[i]]++;
if((tmp[a[i]]&1) && (tmp[a[i]]!=1)) ret--;
else if(!(tmp[a[i]]&1)) ret++;
}
for(int i=x;i<=y;i++) tmp[a[i]]=0;
return ret;
}
int ret=ans[bl[x]+1][bl[y]-1],now;
for(int i=x;i<=r[bl[x]];i++) {
tmp[a[i]]++;now=cnt[a[i]][bl[y]-1]-cnt[a[i]][bl[x]]+tmp[a[i]];
if(now>1 && (now&1)) ret--;
else if(!(now&1)) ret++;
}
for(int i=l[bl[y]];i<=y;i++){
tmp[a[i]]++;now=cnt[a[i]][bl[y]-1]-cnt[a[i]][bl[x]]+tmp[a[i]];
if(now>1 && (now&1)) ret--;
else if(!(now&1)) ret++;
}
for(int i=x;i<=r[bl[x]];i++) tmp[a[i]]--;
for(int i=l[bl[y]];i<=y;i++) tmp[a[i]]--;
return ret;
}
int main(){
n=rd(),c=rd(),m=rd();
siz=sqrt(n)+1;num=n/siz;if(n%siz) num++;
for(int i=1;i<=n;i++)
a[i]=rd(),bl[i]=(i-1)/siz+1;
for(int i=1;i<=num;i++)
l[i]=(i-1)*siz+1,r[i]=i*siz;
r[num]=n;
for(int i=num;i;i--){
int now=0;
for(int j=r[i];j;j--){
cnt[a[j]][i]++;
if((cnt[a[j]][i]&1) && cnt[a[j]][i]!=1) now--;
else if(!(cnt[a[j]][i]&1)) now++;
if(bl[j]!=bl[j-1]) ans[bl[j]][i]=now;
}
}
// for(int i=1;i<=num;i++)
// for(int j=1;j<=c;j++)
// cnt[j][i]+=cnt[j][i-1];
// for(int i=1;i<=num;i++)
// for(int j=l[i];j<=r[i];j++)
// cnt[a[j]][i]++;
// for(int i=1;i<=num;i++)
// for(int j=1;j<=c;j++)
// cnt[j][i]+=cnt[j][i-1];
// for(int i=1;i<=num;i++)
// for(int j=i;j<=num;j++)
// for(int k=1;k<=c;k++)
// if(cnt[k][j]-cnt[k][i-1]>0 && (!((cnt[k][j]-cnt[k][i-1])&1)))
// ans[i][j]++;
int L,R;
while(m--){
L=rd(),R=rd();
L=(Ans+L)%n+1,R=(Ans+R)%n+1;
if(L>R) swap(L,R);
Ans=query(L,R);
printf("%d\n",Ans);
}
return 0;
}