2018.09.30 bzoj2821: 作诗(Poetize)(分块)

传送门
分块经典题目。


先将数列分块。
然后预处理出每两个块之间有多少个数出现了正偶数次。
这样查询的时候对于中间的完整块直接用预处理出的数组搞定。
剩下的暴力枚举求解。
代码:

#include
#define N 100005
using namespace std;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
int n,c,m,a[N],lastans,blo[N],cnt[N],cal[255][N],sum[255][255],sig=500,blos;
inline int query(int ql,int qr){
    int l=blo[ql],r=blo[qr],ret=0;
    if(r-l<2){
        for(int i=ql;i<=qr;++i){
            ++cnt[a[i]];
            if(!(cnt[a[i]]&1))++ret;
            else if(cnt[a[i]]>2)--ret;
        }
        for(int i=ql;i<=qr;++i)--cnt[a[i]];
        return ret;
    }
    ret+=sum[l+1][r-1];
    for(int i=ql;i<=l*sig;++i){
        ++cnt[a[i]];
        int tmp=cnt[a[i]]+cal[r-1][a[i]]-cal[l][a[i]];
        if(!(tmp&1))++ret;
        else if(tmp>2)--ret;
    }
    for(int i=(r-1)*sig+1;i<=qr;++i){
        ++cnt[a[i]];
        int tmp=cnt[a[i]]+cal[r-1][a[i]]-cal[l][a[i]];
        if(!(tmp&1))++ret;
        else if(tmp>2)--ret;
    }
    for(int i=ql;i<=l*sig;++i)--cnt[a[i]];
    for(int i=(r-1)*sig+1;i<=qr;++i)--cnt[a[i]];
    return ret;
}
int main(){
    n=read(),c=read(),m=read();
    for(int i=1;i<=n;++i)++cal[(blo[i]=(i-1)/sig+1)][(a[i]=read())];
    blos=blo[n];
    for(int i=1;i<=c;++i)for(int j=1;j<=blos;++j)cal[j][i]+=cal[j-1][i];
    for(int i=1;i<=blos;++i){
        int tmp=0;
        for(int j=(i-1)*sig+1;j<=n;++j){
            ++cnt[a[j]];
            if(!(cnt[a[j]]&1))++tmp;
            else if(cnt[a[j]]>2)--tmp;
            sum[i][blo[j]]=tmp;
        }
        for(int j=(i-1)*sig+1;j<=n;++j)--cnt[a[j]];
    }
    while(m--){
        int l=(read()+lastans)%n+1,r=(read()+lastans)%n+1;
        if(l>r)swap(l,r);
        printf("%d\n",lastans=query(l,r));
    }
    return 0;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/9738172.html

你可能感兴趣的:(2018.09.30 bzoj2821: 作诗(Poetize)(分块))