BZOJ3530 [Sdoi2014]數數
阿新 • • 發佈:2019-01-26
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]數數