[BZOJ 2821] 作诗

Link:

BZOJ 2821 传送门

Solution:

一道类似区间众数的经典分块

由于个数为偶数这样的条件不能支持快速合并

因此要先$O(n*sqrt(n))$预处理出$pre[i][j]$表示第$i$块到第$j$块的答案

同时要建立每种颜色的有序序列方便求出一个区间内某种颜色的个数

这样每次查询时就能$O(1)$得出整块答案,$O(size*logn)$算出非整块的数对答案的影响

Code:

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef double db;
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10,CNT=1505;
vector<int> col[MAXN];
int n,c,m,l,r,dat[MAXN],pre[CNT][CNT],block;
int L[MAXN],R[MAXN],sub[MAXN],vis[MAXN],sum[MAXN],cnt,res;

void PRE()
{
    for(int i=1;i<=cnt;i++)
    {
        int cur=0;
        for(int j=L[i];j<=n;j++)
            sum[dat[j]]=0;
        for(int j=L[i];j<=n;j++)
        {
            if(!(sum[dat[j]]&1)&&sum[dat[j]]) cur--;
            sum[dat[j]]++;
            if(!(sum[dat[j]]&1)) cur++;
            pre[i][sub[j]]=cur;
        }
    }
    for(int i=1;i<=n;i++)
        col[dat[i]].pb(i);
    for(int i=1;i<=c;i++)
        if(col[i].size()) sort(col[i].begin(),col[i].end());
}

int cal(int x,int l,int r)
{
    int lft=lower_bound(col[x].begin(),col[x].end(),l)-col[x].begin();
    int rgt=upper_bound(col[x].begin(),col[x].end(),r)-col[x].begin()-1;
    return max(rgt-lft+1,0);
}
void work(int pos,int &ret,int l,int r,int x,int y)
{
    if(vis[dat[pos]]) return;
    int t1=cal(dat[pos],l,r),t2=cal(dat[pos],x,y);
    //注意这里的判断 
    if(!(t1&1)&&(!t2||t2&1)) ret++;
    else if((t1&1)&&!(t2&1)&&t2)ret--;
    vis[dat[pos]]=1;
}
int solve(int l,int r)
{
    int bl=sub[l],br=sub[r],ret=0;
    if(bl==br||bl+1==br)
    {
        for(int i=l;i<=r;i++)
        {
            if(vis[dat[i]]) continue;
            int t=cal(dat[i],l,r);
            ret+=(!(t&1));vis[dat[i]]=1;
        }
        for(int i=l;i<=r;i++) vis[dat[i]]=0;
    }
    else
    {
        ret=pre[bl+1][br-1];
        for(int i=l;i<L[bl+1];i++)
            work(i,ret,l,r,L[bl+1],R[br-1]);
        for(int i=R[br-1]+1;i<=r;i++)
            work(i,ret,l,r,L[bl+1],R[br-1]);    
        for(int i=l;i<L[bl+1];i++) vis[dat[i]]=0;
        for(int i=R[br-1]+1;i<=r;i++) vis[dat[i]]=0;
    }
    return ret;
}

int main()
{
    scanf("%d%d%d",&n,&c,&m);
    block=sqrt((double)n/log((double)n)*log(2));
    cnt=n/block+(n%block!=0);
    for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
    for(int i=1;i<=n;i++) sub[i]=(i-1)/block+1;
    for(int i=1;i<=cnt;i++)
        L[i]=(i-1)*block+1,R[i]=i*block;
    R[cnt]=n;PRE();
    
    while(m--)
    {
        scanf("%d%d",&l,&r);
        l=(l+res)%n+1;r=(r+res)%n+1;
        if(l>r) swap(l,r);
        printf("%d\n",res=solve(l,r));
    }
    return 0;
}

 

posted @ 2018-09-28 11:09  NewErA  阅读(122)  评论(0编辑  收藏  举报