1 条题解

  • 0
    @ 2024-2-18 15:38:50

    首先容易发现操作可以加强为对 a\geq ab\geq b 的段进行修改。由于可以直接把整个串改为 1,所以 a,ba,b 可以交换,不妨设 aba\leq b

    把操作反过来,考虑对于一个 01 序列,能否经过若干次逆操作变为全为 0

    原操作的逆操作就是选择一个长度 a\geq a 的全为 0 的段变为任意数。因为我们现在不知道变为什么数对我们有利,我们不妨把它们变为 ?,这样我们可以选择一个长度 a\geq a 的不包含 1 的段全变为 ?,或选择一个长度 b\geq b 的不包含 0 的段全变为 ?。而目标就是把整个序列变为 0?,等价于变为全部是 ?

    由于 ? 的功能严格包含 01 的功能,所以我们贪心地只要遇到长 a\geq a 的非 1 段就变为 ?

    注意到如果我们把一个长为 bb 的段变为了 ?,由于 aba\leq b,我们可以把这个 ? 段无限延长,直至整个序列都变为 ?

    于是我们得出了如下结论:一个序列能否变为全 0 串,等价于把其中所有长度至少为 aa 的极长连续 1 段全变为 0 后是否存在长至少为 bb 的连续 0 段。

    由容斥原理,只需求用长 <a< a 的极长连续 1 段分割后每段长 <b< b 且所有极长连续 1 段的长度都 a\geq a

    考虑动态规划,记

    • fif_i 表示到第 ii 位为止最后一个连续段是长度小于 aa1 段;
    • gi,jg_{i,j} 表示到第 ii 位为止有一个长度为 jj 不包含长度小于 aa 的极长连续 1 段的极长段且最后一个连续段是 0
    • hi,jh_{i,j} 表示到第 ii 位为止有一个长度为 jj 不包含长度小于 aa 的极长连续 1 段的极长段且最后一个连续段是 1

    用朴素方法转移是 Θ(n3)\Theta(n^3) 的,使用前缀和优化即可做到 Θ(n2)\Theta(n^2)

    /**
     * @date: 2024.02.18
     * @problem: AT_agc045_c
     * @tags: 数学, 组合数学, 组合计数, 思维, 动态规划
     */
    
    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    
    int n,a,b;
    long long f[5001],g[5001][5001],h[5001][5001];
    long long sg[5001],sumg[5001][5001],sumh[5001];
    
    int main(){
        scanf("%d%d%d",&n,&a,&b);
        if(a>b)swap(a,b);
        f[0]=g[0][0]=h[0][0]=1;
        if(n<=1000){
            // O(N^3)
            for(int i=1;i<=n;i++){
                if(i<a)f[i]++;
                for(int j=max(0,i-a+1);j<i;j++)
                    for(int k=1;k<=j;k++)
                        f[i]+=g[j][k],f[i]%=mod;
                for(int j=1;j<=i&&j<b;j++){
                    for(int k=1;k<j;k++)
                        g[i][j]+=h[i-k][j-k];
                    g[i][j]+=f[i-j],g[i][j]%=mod;
                }
                if(a<=i&&i<b)h[i][i]++;
                for(int j=1;j<=i&&j<b;j++){
                    for(int k=a;k<j;k++)
                        h[i][j]+=g[i-k][j-k];
                    h[i][j]%=mod;
                }
            }
        }
        else{
            // O(N^2)
            for(int i=1;i<=n;i++){
                for(int j=1;j<i;j++)
                    sg[i-1]+=g[i-1][j];
                sg[i-1]%=mod;
    
                if(i<a)f[i]++;
                for(int j=max(0,i-a+1);j<i;j++)f[i]+=sg[j];
                f[i]%=mod;
                for(int j=1;j<=i&&j<b;j++){
                    g[i][j]=(sumh[i-j]+f[i-j])%mod;
                    sumg[i][j]=(sumg[i-1][j-1]+g[i][j])%mod;
                }
                if(a<=i&&i<b)h[i][i]++;
                for(int j=1;j<=i&&j<b;j++){
                    if(i>=a&&j>=a)h[i][j]+=sumg[i-a][j-a],h[i][j]%=mod;
                    sumh[i-j]+=h[i][j],sumh[i-j]%=mod;
                }
            }
        }
        long long answer=1;
        for(int i=1;i<=n;i++)answer=answer*2%mod;
        answer-=f[n];
        for(int i=1;i<=n;i++)
            answer-=g[n][i]+h[n][i];
        printf("%lld\n",(answer%mod+mod)%mod);
        return 0;
    }
    
    • 1

    信息

    ID
    2281
    时间
    2000ms
    内存
    1024MiB
    难度
    6
    标签
    (无)
    递交数
    7
    已通过
    2
    上传者