1. 程式人生 > >BZOJ3530 [Sdoi2014]數數

BZOJ3530 [Sdoi2014]數數

inline sca 不包含 queue efi [1] blog 大於 namespace

題意

我們稱一個正整數N是幸運數,當且僅當它的十進制表示中不包含數字串集合S中任意一個元素作為其子串。例如當S=(22,333,0233)時,233是幸運數,2333、20233、3223不是幸運數。

給定N和S,計算不大於N的幸運數個數。

分析

參照租酥雨的題解。

AC自動機做數位DP。首先位數小於\(n\)的位數的數只要滿足沒有不合法串即可,記\(f[i][j]\)表示填了\(i\)個數,當前在AC自動機上編號為\(j\)的節點上的方案數,取答案\(\sum^{n?1}_{i=1}\sum^{tot}_{j=0}f[i][j]\)。註意轉移的時候是只能轉移到自己Trie圖上的兒子而不是兒子通過fail指針串起來的所有點。

位數等於\(n\)的,在前面那個狀態上多加一維,表示是否已經嚴格小於那個數,然後按照數位DP的一般思路卡一卡就好了。

第二次DP的狀態是\(f[0/1][i][j]\),0/1是數位dp的那個是否之前所有位都和N相同。

時間復雜度\(O(10 * l * L)\),是18000000。

代碼

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
    rg T data=0;
    rg int w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<class T>il T read(rg T&x)
{
    return x=read<T>();
}
typedef long long ll;

co int mod=1e9+7,N=1501;

int add(int x,int y)
{
    x+=y;
    return x>mod?x-mod:x;
}

int n;
char s[N],buf[N];
namespace AC
{
    int tot;
    int ch[N][10],val[N],fail[N];
    
    void ins(char s[],int n)
    {
        int u=0;
        for(int i=0;i<n;++i)
        {
            int k=s[i]-'0';
            if(!ch[u][k])
                ch[u][k]=++tot;
            u=ch[u][k];
        }
        val[u]=1;
    }
    
    void getfail()
    {
        std::queue<int>Q;
        for(int i=0;i<10;++i)
            if(ch[0][i])
                Q.push(ch[0][i]);
        while(Q.size())
        {
            int u=Q.front();Q.pop();
            val[u]|=val[fail[u]];
            for(int i=0;i<10;++i)
            {
                if(ch[u][i])
                {
                    fail[ch[u][i]]=ch[fail[u]][i];
                    Q.push(ch[u][i]);
                }
                else
                    ch[u][i]=ch[fail[u]][i];
            }
        }
    }
    
    int dp[2][N][N];
    
    void solve()
    {
        int ans=0;
        dp[0][0][0]=1;
        for(int i=0;i<n;++i)
            for(int j=0;j<=tot;++j)if(!val[j])
                for(int k=0;k<10;++k)if(i+k&&!val[ch[j][k]])
                    dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[0][i][j]);
        for(int i=1;i<n;++i)
            for(int j=0;j<=tot;++j)
                ans=add(ans,dp[0][i][j]);
        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(!val[j])
                for(int k=0;k<10;++k)if(i+k&&!val[ch[j][k]])
                {
                    dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[0][i][j]);
                    if(k==s[i+1]-'0')
                        dp[1][i+1][ch[j][k]]=add(dp[1][i+1][ch[j][k]],dp[1][i][j]);
                    else if(k<s[i+1]-'0')
                        dp[0][i+1][ch[j][k]]=add(dp[0][i+1][ch[j][k]],dp[1][i][j]);
                }
        for(int j=0;j<=tot;++j)
            ans=add(ans,add(dp[0][n][j],dp[1][n][j]));
        printf("%d\n",ans);
    }
}

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    scanf("%s",s+1);
    n=strlen(s+1);
    int m;
    read(m);
    for(int i=1;i<=m;++i)
    {
        scanf("%s",buf);
        AC::ins(buf,strlen(buf));
    }
    AC::getfail();
    AC::solve();
    return 0;
}

BZOJ3530 [Sdoi2014]數數