BZOJ3530: [Sdoi2014]数数(Trie图,数位Dp)
Description
我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。
给定N和S,计算不大于N的幸运数个数。
Input
输入的第一行包含整数N。
接下来一行一个整数M,表示S中元素的数量。
接下来M行,每行一个数字串,表示S中的一个元素。
Output
输出一行一个整数,表示答案模109+7的值。
Sample Input
20
3
2
3
14
3
2
3
14
Sample Output
14
解题思路:
很明显是个数位Dp,相当于n位的不要62,由于不要62的字符个数是可以枚举的,这个不可以。
设计一个状态dp[i][j][onlim(0/1)][zero(0/1)]来表示字符到了 i 位,Trie图上到了 j 号节点,是否压了上线,是否有前导零。
转移则是寻找Trie图上一个子节点,如果不代表字符的结束,那么进一位,判断是否压上界,是否为零即可。
注意子节点为root时不向下转移Trie图(即失配)
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 typedef long long lnt; 5 const lnt mod=(lnt)(1e9+7); 6 struct trnt{ 7 int ch[10]; 8 int fl; 9 bool fin; 10 }tr[10000]; 11 class queue{ 12 public: 13 queue(void){h=1,t=0;return ;} 14 int nxt(int x){if(x+1==100000)return 1;return x+1;} 15 int front(void){return line[h];} 16 void pop(void){h=nxt(h);return ;} 17 void push(int x){t=nxt(t);line[t]=x;return ;} 18 bool empty(void){return nxt(t)==h;} 19 private: 20 int h,t,line[100000]; 21 }Q; 22 int siz; 23 int n,m; 24 int l; 25 char tmp[1000000]; 26 int num[1201]; 27 lnt dp[1201][1501][2][2]; 28 void Insert(char *a) 29 { 30 int root=0; 31 int len=strlen(a+1); 32 for(int i=1;i<=len;i++) 33 { 34 int c=a[i]-'0'; 35 if(!tr[root].ch[c]) 36 tr[root].ch[c]=++siz; 37 root=tr[root].ch[c]; 38 } 39 tr[root].fin=true; 40 return ; 41 } 42 void Build(void) 43 { 44 int root=0; 45 for(int i=0;i<10;i++) 46 if(tr[root].ch[i]) 47 Q.push(tr[root].ch[i]); 48 while(!Q.empty()) 49 { 50 root=Q.front(); 51 Q.pop(); 52 tr[root].fin|=tr[tr[root].fl].fin; 53 for(int i=0;i<10;i++) 54 if(tr[root].ch[i]) 55 { 56 tr[tr[root].ch[i]].fl=tr[tr[root].fl].ch[i]; 57 Q.push(tr[root].ch[i]); 58 }else 59 tr[root].ch[i]=tr[tr[root].fl].ch[i]; 60 } 61 return ; 62 } 63 int main() 64 { 65 scanf("%s",tmp+1); 66 l=strlen(tmp+1); 67 for(int i=1;i<=l;i++) 68 num[i]=tmp[i]-'0'; 69 scanf("%d",&m); 70 for(int i=1;i<=m;i++) 71 { 72 scanf("%s",tmp+1); 73 Insert(tmp); 74 } 75 Build(); 76 dp[0][0][1][1]=1; 77 for(int i=1;i<=l;i++) 78 { 79 for(int j=0;j<=siz;j++) 80 { 81 for(int onlim=0;onlim<2;onlim++) 82 { 83 for(int zero=0;zero<2;zero++) 84 { 85 if(!dp[i-1][j][onlim][zero]) 86 continue; 87 int lim=onlim*num[i]+(1-onlim)*9; 88 for(int c=0;c<=lim;c++) 89 { 90 if(tr[tr[j].ch[c]].fin) 91 continue; 92 int nwlim=onlim&&(c==lim); 93 int nwzro=zero&&(!c); 94 int nwplc=(1-nwzro)*tr[j].ch[c]; 95 dp[i][nwplc][nwlim][nwzro]=(dp[i][nwplc][nwlim][nwzro]+dp[i-1][j][onlim][zero])%mod; 96 } 97 } 98 } 99 } 100 } 101 lnt ans=0; 102 for(int i=0;i<=siz;i++) 103 ans=(ans+dp[l][i][1][0]+dp[l][i][0][0])%mod; 104 printf("%lld\n",(ans+mod)%mod); 105 return 0; 106 }