zoukankan      html  css  js  c++  java
  • P3311 [SDOI2014]数数

    传送门

    显然直接 $AC$ 自动机上数位 $dp$ 一下

    预处理出 $f[i][j]$ 表示当前匹配到 $AC$ 自动机上的节点 $j$ ,再放 $i$ 个位的数字后不冲突的方案数

    初始时 $f[0][j]=1$ ,其中 $j$ 不是匹配节点(匹配节点显然指的是本身是某个模式串的结束节点或者 $fail$ 树上祖先存在某个节点是结束节点)

    然后把 $n$ 放到自动机上走,其实就是普通的数位 $dp$ ,每到一个节点都枚举所有小于这一位的数字,然后把 $f$ 相应的方案数加入答案

    然后一旦 $n$ 走到自动机的匹配节点了就直接 $return$

    如果 $n$ 走完了也没有到匹配节点,那么等于 $n$ 的这个数也是一种合法方案,要记得答案 $+1$,那么数位 $dp$ 的代码很简单:

    void dfs(int x,int p)//当前在节点x,考虑到n的第p位
    {
        if(pd[x]) return;//不合法了直接return
        if(p>tot) { ans++; return; }//n是合法的答案要包括n
        for(int i=0;i<n[p]-'0';i++) ans=(ans+f[tot-p][c[x][i]])%mod;//枚举这一位小于n[p]的数,剩下的位就可以随便填
        dfs(c[x][n[p]-'0'],p+1);
    }

    然后发现只有 $80$ 分,这是因为模式串可能有前导 $0$ ,我们预处理 $f$ 的时候也是有考虑前导 $0$ 的情况,但是真正填数的时候是没有前导零的

    所以在 $n$ 的第一位之前,我们枚举小于 $n[1]$ 的数时不能枚举 $0$ ,因为这样就变成了有前导零的情况

    我们必须在最后再特殊考虑位数不足 $n$ 的情况:

    for(int i=0;i<tot-1;i++)//枚举总位数不足n的位数的情况
        for(int j=1;j<=9;j++) ans=(ans+f[i][c[0][j]])%mod;//枚举第一个数
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    typedef long long ll;
    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=2007,mo=1e9+7;
    inline int fk(int x) { return x>=mo ? x-mo : x; }
    int tot,m;
    char s[N],n[N];
    int c[N][11],fail[N],cnt;
    bool pd[N];
    void ins()
    {
        int u=0,len=strlen(s+1);
        for(int i=1;i<=len;i++)
        {
            int v=s[i]-'0';
            if(!c[u][v]) c[u][v]=++cnt;
            u=c[u][v];
        }
        pd[u]=1;
    }
    void build()
    {
        queue <int> Q;
        for(int i=0;i<=9;i++) if(c[0][i]) Q.push(c[0][i]);
        while(!Q.empty())
        {
            int x=Q.front(); Q.pop();
            for(int i=0;i<=9;i++)
            {
                int &v=c[x][i];
                if(!v) v=c[fail[x]][i];
                else fail[v]=c[fail[x]][i],pd[v]|=pd[fail[v]],Q.push(v);
            }
        }
    }
    int f[N][N],ans;
    void pre()
    {
        for(int i=0;i<=cnt;i++) if(!pd[i]) f[0][i]=1;
        for(int i=1;i<=tot;i++)
            for(int j=0;j<=cnt;j++)
            {
                if(pd[j]) continue;
                for(int k=0;k<=9;k++)
                    f[i][j]=fk(f[i][j]+f[i-1][c[j][k]]);
            }
    }
    void dfs(int x,int p)
    {
        if(pd[x]) return;
        if(p>tot) { ans++; return; }
        for(int i=(p==1);i<n[p]-'0';i++) ans=fk(ans+f[tot-p][c[x][i]]);
        dfs(c[x][n[p]-'0'],p+1);
    }
    int main()
    {
        scanf("%s",n+1); m=read(); tot=strlen(n+1);
        for(int i=1;i<=m;i++) scanf("%s",s+1),ins();
        build(); pre(); dfs(0,1);
        for(int i=0;i<tot-1;i++)
            for(int j=1;j<=9;j++) ans=fk(ans+f[i][c[0][j]]);
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 3037 Saving Beans【Lucas定理】【模板题】【模板】【组合数取余】
    8.Math 对象
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11567758.html
Copyright © 2011-2022 走看看