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;
    }
  • 相关阅读:
    appium自动化测试(4)部分方法&unitest初步使用
    appium自动化测试(2)-工具(monitor、uiautomatorviewer)
    Appium自动化测试(1)-安装&环境
    echarts 地图 免费离线js,json包分享
    css动画Demo---水波动画和边框动画
    canvas绘制折线图(仿echarts)
    可编辑div中包含子元素时获取光标位置不准确的问题
    脚印
    从原理到代码之线性模型
    【ocelot】ocelot使用swagger
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11567758.html
Copyright © 2011-2022 走看看