【bzoj 2821】 作诗 题意&题解&代码(C++)

题目链接:

http://www.lydsy.com/JudgeOnline/problem.php?id=2821

权限题,附题意:
【bzoj 2821】 作诗 题意&题解&代码(C++)_第1张图片
【bzoj 2821】 作诗 题意&题解&代码(C++)_第2张图片
【bzoj 2821】 作诗 题意&题解&代码(C++)_第3张图片

题解:

分块题,他要求在线处理区间问题,很明显是为了卡莫队这种神奇算法,首先将序列分块,按 sqrt(n) (即根号n)分块,然后预处理出一个数组 f[i][j] 表示从第 i 块到第 j 块内,满足题意的答案,还要处理出另一个数组 cnt[i][j] 表示前 i 块中数字 j 出现了多少次,(不要担心数组太大开不下,三千两百万它还真就是存下了,o.o!),这样的话当询问区间 (i,j)时,我们利用 f[i][j] 通过各种判断首先将包含在这个区间里的块的答案统计出来,再利用 cnt[i][j] 将两头或是一头没有达到一整快的部分暴力的统计,一边统计一边修改已经得到的答案即可,时限还是够的。

注意题中一个特别坑的地方,

他特别心机的的在mod与n+1 之间加了一个空格,让大家以为是mod(n+1)实际上却是(mod n)+1。。2333

代码

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<math.h>
#define maxn (100005)
#define maxs (320)
using namespace std;
int vis[maxn],ans,L,R,len,n,num,c,m,sq[maxn],f[maxs][maxs],cnt[maxs][maxn],a[maxn],st[maxs],tmp[maxn];
void solve(int l,int r)//计算 l,r
{
    if (sq[l]==sq[r] || sq[l]+1==sq[r])
    {
        for (int i=l;i<=r;i++)
        {
            vis[a[i]]++;
            if (vis[a[i]]%2==0) ans++;
            else if (vis[a[i]]!=1) ans--;
        }
        for (int i=l;i<=r;i++)
        vis[a[i]]=0;
        return ;
    }
    if (l==st[sq[l]] && r==st[sq[r]+1]-1)
    {
        ans=f[sq[l]][sq[r]];
        return ;    
    }
    else if (l==st[sq[l]])
    {
        ans=f[sq[l]][sq[r]-1];
        for (int i=st[sq[r]];i<=r;i++)
        vis[a[i]]=cnt[sq[r]-1][a[i]]-cnt[sq[l]-1][a[i]];
        for (int i=st[sq[r]];i<=r;i++)
        {
            vis[a[i]]++;
            if (vis[a[i]]%2==0) ans++;
            else if (vis[a[i]]!=1) ans--;       
        }
                for (int i=st[sq[r]];i<=r;i++)
                vis[a[i]]=0;
        return ;                
    }
    else if (r==st[sq[r]+1]-1)
    {
        ans=f[sq[l]+1][sq[r]];
        for (int i=l;i<st[sq[l]+1];i++)
        vis[a[i]]=cnt[sq[r]][a[i]]-cnt[sq[l]][a[i]];
        for (int i=l;i<st[sq[l]+1];i++)
        {
            vis[a[i]]++;
            if (vis[a[i]]%2==0) ans++;
            else if (vis[a[i]]!=1) ans--;   
        }
                for (int i=l;i<st[sq[l]+1];i++)
                vis[a[i]]=0;
        return ;
    }
    else
    {
        int x=sq[l]+1;
        int y=sq[r]-1;
        ans=f[x][y];
        for (int i=l;i<st[x];i++)
        vis[a[i]]=cnt[y][a[i]]-cnt[x-1][a[i]];

        for (int i=st[y+1];i<=r;i++)
        vis[a[i]]=cnt[y][a[i]]-cnt[x-1][a[i]];

        for (int i=l;i<st[x];i++)
        {
            vis[a[i]]++;
            if (vis[a[i]]%2==0) ans++;
            else if (vis[a[i]]!=1) ans--;   
        }

        for (int i=st[y+1];i<=r;i++)
        {
            vis[a[i]]++;
            if (vis[a[i]]%2==0) ans++;
            else if (vis[a[i]]!=1) ans--;       
        }   
        for (int i=l;i<st[x];i++) vis[a[i]]=0;
        for (int i=st[y+1];i<=r;i++) vis[a[i]]=0;   
        return ;
    }
}
int main()
{
    scanf("%d%d%d",&n,&c,&m);
    len=sqrt(n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sq[i]=(i-1)/len+1;
        cnt[sq[i]][a[i]]++;
        if (sq[i]!=sq[i-1])
        st[sq[i]]=i;
    }
    num=sq[n];

    for (int i=1;i<=num;i++)
    for (int j=1;j<=c;j++)
    cnt[i][j]+=cnt[i-1][j];
    //预处理 cnt数组
    for (int i=1;i<=num;i++)
    {
        int tans=0;
        for (int j=1;j<=c;j++)
        tmp[j]=0;
        for (int j=st[i];j<=n;j++)
        {
            tmp[a[j]]++;
            if (tmp[a[j]]%2==0) tans++;
            else if (tmp[a[j]]!=1) tans--;
            f[i][sq[j]]=tans;
        }
    }//预处理 f 数组
    ans=0;

    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&L,&R);
        L=(L+ans)%n+1;
        R=(R+ans)%n+1;
        ans=0;
        if (L>R) swap(L,R);
        solve(L,R);
        printf("%d\n",ans);
    }
}

不得不说,csdn的博客编辑最近好像十分的恶心。。。

你可能感兴趣的:(算法,分块)