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;
}

猜你喜欢

转载自www.cnblogs.com/sdfzsyq/p/10127633.html