• [BZOJ3530][SDOI2014]数数


    BZOJ
    Luogu

    sol

    AC自动机做数位(DP)。首先位数小于(n)的位数的数只要满足没有不合法串即可,记(f_{i,j})表示填了(i)个数,当前在(AC)自动机上编号为(j)的节点上的方案数,取答案(sum_{i=1}^{n-1}sum_{j=0}^{tot}f_{i,j})。注意转移的时候是只能转移到自己(Trie)图上的儿子而不是儿子通过(fail)指针串起来的所有点。
    位数等于(n)的,在前面那个状态上多加一维,表示是否已经严格小于那个数,然后按照数位(DP)的一般思路卡一卡就好了。

    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int mod = 1e9+7;
    const int N = 2005;
    int gi()
    {
        int x=0,w=1;char ch=getchar();
        while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if (ch=='-') w=0,ch=getchar();
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return w?x:-x;
    }
    
    int n,m,l,tot,ch[10][N],fail[N],frb[N],dp[2][N][N],ans;
    char s[N],c[N];
    queue<int>Q;
    void Insert()
    {
        scanf("%s",c);l=strlen(c);
        int x=0;
        for (int i=0;i<l;i++)
        {
            if (!ch[c[i]-'0'][x]) ch[c[i]-'0'][x]=++tot;
            x=ch[c[i]-'0'][x];
        }
        frb[x]=1;
    }
    void Get_Fail()
    {
        for (int i=0;i<10;i++) if (ch[i][0]) Q.push(ch[i][0]);
        while (!Q.empty())
        {
            int u=Q.front();Q.pop();
            for (int i=0;i<10;i++)
                if (ch[i][u]) fail[ch[i][u]]=ch[i][fail[u]],Q.push(ch[i][u]);
                else ch[i][u]=ch[i][fail[u]];
            frb[u]|=frb[fail[u]];
        }
    }
    void DP()
    {
        dp[0][0][0]=1;
        for (int i=0;i<n;i++)
            for (int j=0;j<=tot;j++)
                if (!frb[j])
                    for (int k=0;k<10;k++)
                        if (i+k&&!frb[ch[k][j]])
                            (dp[0][i+1][ch[k][j]]+=dp[0][i][j])%=mod;
        for (int i=1;i<n;i++)
            for (int j=0;j<=tot;j++)
                (ans+=dp[0][i][j])%=mod;
        memset(dp,0,sizeof(dp));
        dp[1][0][0]=1;
        for (int i=0;i<n;i++)
            for (int j=0;j<=tot;j++)
                if (!frb[j])
                    for (int k=0;k<10;k++)
                        if (i+k&&!frb[ch[k][j]])
                        {
                            (dp[0][i+1][ch[k][j]]+=dp[0][i][j])%=mod;
                            if (k==s[i+1]-'0') (dp[1][i+1][ch[k][j]]+=dp[1][i][j])%=mod;
                            if (k<s[i+1]-'0') (dp[0][i+1][ch[k][j]]+=dp[1][i][j])%=mod;
                        }
        for (int j=0;j<=tot;j++) (ans+=(dp[0][n][j]+dp[1][n][j])%mod)%=mod;
    }
    int main()
    {
        scanf("%s",s+1);n=strlen(s+1);
        scanf("%d",&m);
        for (int i=1;i<=m;i++) Insert();
        Get_Fail();
        DP();
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    【转】P2P通信原理与实现(C++)
    【转】P2P通信标准协议(二)之TURN
    【转】P2P之UDP穿透NAT的原理与实现
    【转】P2P的原理和常见的实现方式
    【转】linux中man使用技巧
    【转】go编译时,加入svn版本信息
    各种移动GPU压缩纹理的使用方法
    Unity贴图压缩格式设置
    关于U3D贴图格式压缩
    可能会导致.NET内存泄露的8种行为
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8350090.html
Copyright © 2020-2023  润新知