P4135 作诗

Posted lltyyc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4135 作诗相关的知识,希望对你有一定的参考价值。

传送门

分块

设sum[ i ] [ j ] 存从左边到第 i 块时,数字 j 的出现次数

 f [ i ] [ j ] 存从第 i 块,到第 j 块的一整段的答案

那么最后答案就是一段区间中几块整段的答案加上两边小段的贡献

考虑两边小段的影响,对于每一个出现的数

它可能会使答案增加(使原本大区间中出现奇数次的数变成出现偶数次)

也可能使答案减少(使原本大区间中出现偶数次的数变成出现奇数次)

有了 sum 数组我们可以很方便地求出大区间中每个数的出现次数

对小段的每个数直接计算一下它对答案的贡献

开一个 cnt[ i ] 记录一下之前每个数 i 出现的次数就好了

技术分享图片
//bl,br是大区间的左右边界的块
ans=f[bl][br];//ans初值为大区间的答案
for(int i=l;i<L[bl];i++)//L[i]存第i块的左端点
{
    cnt[a[i]]++;//记录a[i]在小段出现的次数
    t=cnt[a[i]]+sum[br][a[i]]-sum[bl-1/*注意是bl-1*/][a[i]];//计算此时a[i]出现的次数
    if(!(t&1)) ans++;//如果a[i]出现的次数变成了偶数次,答案就加一
    else if(t>1) ans--;//否则如果出现次数超过2次且使出现次数变成奇数次,答案减1
    //要特殊考虑t=1的情况,t=1时不会对答案有影响,因为t=0时不算出现偶数次
}
for(int i=L[br+1];i<=r;i++)//注意i的范围
{
    cnt[a[i]]++; t=cnt[a[i]]+sum[br][a[i]]-sum[bl-1][a[i]];
    if(!(t&1)) ans++;
    else if(t>1) ans--;
}
for(int i=l;i<L[bl];i++) cnt[a[i]]--; for(int i=L[br+1];i<=r;i++) cnt[a[i]]--;//别忘记还原cnt,以后询问还要用,注意不要用memset
关于询问

 

然后来考虑如何预处理

sum数组很好求,关键是 f

好像枚举每个块复杂度会爆炸...

我们来看看处理询问时的方法,对每个数都计算贡献

预处理就相当于询问每两个块之间的贡献

我们可以用同样的方法,cnt[ i ] 记录 i 出现了几次

从左到右扫,计算每个数对答案的贡献,如果扫到一个区间的右端点了,就记录一波 f

 

技术分享图片
//bel[i]表示点i属于第几个块
for(int i=1;i<=bel[n];i++)//枚举每个左块
{
    t=0;
    for(int j=L[i];j<=n;j++)//考虑右边所有块
    {
        cnt[a[j]]++;//记录
        if(!(cnt[a[j]]&1)) t++;
        else if(cnt[a[j]]>1) t--;
        //考虑对答案的贡献
        if(bel[j]!=bel[j+1]) f[i][bel[j]]=t;//如果到了一个区间的右端点,更新f
    }
    for(int j=L[i];j<=n;j++) cnt[a[j]]--;//别忘了还原cnt
}
关于预处理

 

这样我们就可以在 O( n*sqrt(n) )的时间内预处理,在 O( sqrt(n)+2*sqrt(n) ) 的时间处理询问

注意一下常数就轻松过了

细节很多的一题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<0||ch>9)
    {
        if(ch==-) f=-1;
        ch=getchar();
    }
    while(ch>=0&&ch<=9)
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int N=1e5+7,M=327;
int n,m,q,ans;
int a[N],bel[N],L[M];
int sum[M][N],f[M][M];

int cnt[N];
inline void pre()//预处理
{
    int t=0;
    for(int i=1;i<=bel[n];i++)
        for(int j=1;j<=m;j++) sum[i][j]+=sum[i-1][j];
    for(int i=1;i<=bel[n];i++)
    {
        t=0;
        for(int j=L[i];j<=n;j++)
        {
            cnt[a[j]]++;
            if(!(cnt[a[j]]&1)) t++;
            else if(cnt[a[j]]>1) t--;
            if(bel[j]!=bel[j+1]) f[i][bel[j]]=t;
        }
        for(int j=L[i];j<=n;j++) cnt[a[j]]--;
    }
}
inline void query(int l,int r)//处理询问
{
    ans=0; int t=0;
    if(bel[l]+1>=bel[r])//对没有大区间的情况特殊处理
    {
        for(int i=l;i<=r;i++)//直接爆力计算每个数的贡献
        {
            cnt[a[i]]++;
            if(!(cnt[a[i]]&1)) ans++;
            else if(cnt[a[i]]>1) ans--;
        }
        for(int i=l;i<=r;i++) cnt[a[i]]--;//清空cnt
        return;
    }
    int bl=bel[l-1]+1,br=bel[r+1]-1;//计算bl,br,细节
    //l-1是考虑当l在一个块最左边的时候,那l在的块整块都会每计算,直接整个拿出来计算就好了,r+1同理
    ans=f[bl][br];//初值
    for(int i=l;i<L[bl];i++)//对左边的小段计算贡献
    {
        cnt[a[i]]++; t=cnt[a[i]]+sum[br][a[i]]-sum[bl-1][a[i]];
        if(!(t&1)) ans++;
        else if(t>1) ans--;
    }
    for(int i=L[br+1];i<=r;i++)//对右边的小段计算贡献
    {
        cnt[a[i]]++; t=cnt[a[i]]+sum[br][a[i]]-sum[bl-1][a[i]];
        if(!(t&1)) ans++;
        else if(t>1) ans--;
    }
    for(int i=l;i<L[bl];i++) cnt[a[i]]--; for(int i=L[br+1];i<=r;i++) cnt[a[i]]--;//清空
    return;
}
int main()
{
    n=read(); m=read(); q=read();
    int t=sqrt(n)+1;
    for(int i=1;i<=n;i++)
    {
        a[i]=read(); bel[i]=(i-1)/t+1;//处理bel
        if(bel[i]!=bel[i-1]) L[bel[i]]=i;//处理L
        sum[bel[i]][a[i]]++;//此时sum只包括第i块的数量
    }
    bel[n+1]=bel[n]+1; L[bel[n+1]]=n+1;//重要的细节,有时我的代码考虑边界时会访问到n+1的点

    pre();

    int l,r;
    for(int i=1;i<=q;i++)
    {
        l=read(); r=read();
        l=(l+ans)%n+1; r=(r+ans)%n+1;
        if(l>r) swap(l,r);//处理l,r
        query(l,r);
        printf("%d
",ans);
    }
    return 0;
}

 

以上是关于P4135 作诗的主要内容,如果未能解决你的问题,请参考以下文章

P4135 作诗

「luogu4135」作诗

让AI生成AI绘画提示词,OpenAI最新成果ChatGPT被网友玩坏了!还会写代码修bug作诗...

BZOJ2821: 作诗(Poetize)

[BZOJ2821]作诗(Poetize)

深度学习100例—— 使用pytorch实现LSTM自动AI作诗(藏头诗和首句续写)| 第6例