传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2243
题目大意:
给出n个单词,问存在多少长度小于l的字符串,子串不含任意一个单词,答案对$2^{64}$取模。
细节多如牛毛的一道题,可以看成是poj2778升级版,长度为定值l的做法,见https://www.cnblogs.com/FZUzyz/p/12885877.html,只是将字母数量从4个推广到了26个而已,没有别的变化。然后是这个坑爹模数,2的64次方,刚开始上了int128,因为两个乘起来的话long long绝对爆了,后面看了部分题解,发现直接开unsigned long long即可,因为ull就是64位,也不需要取模,自然溢出的结果就是他的答案,很巧妙有木有。
随后的思路和poj2778一样,求出合法数量,总数量减去非法数量即可。先求总数量,对于长度小于l的,其数量应该为$sum_{i=1}^{l}26^{i}$,很明显是个等比数列求和公式对吧,是不是很心动,想用等比数列求和公式$sum=frac{26(26^{l}-1)}{25}$,没错,我就这么写的,然后样例都跑不出来,因为有除法,需要求逆元,而模数$2^{64}$又不是质数!所以另求他法,假设f(n)为长度为1-n的串数量总和,不难发现$f(n)=26f(n-1)+26$,这个式子很明显就可以用矩阵快速幂求了。那么总数量的问题就解决了。接下来求合法数量了,要求小于l的总和,显然是要求出$sum_{i=1}^{l}sum_{j=1}^{size}mat[0][j]$,mat为路径矩阵形式,size为矩阵大小。可以构造矩阵
egin{pmatrix}
mat & 1\
0 & 1
end{pmatrix}
注意改造后的矩阵mat2大小为mat矩阵大小+1,将其右侧全部改为1,下方改为0(右下为1除外),这样一来,$mat2^{l}$就变成了
egin{pmatrix}
mat^{l} & sum_{i=1}^{l-1}sum_{j=1}^{size}mat[0][j]\
0 & 1
end{pmatrix}
证明很容易,数学归纳法或者别的方法都可以证明。所以最后只需要将$mat2^{l}$第首行元素求和即可。
最后贴上AC代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; typedef pair <ll,ll> pii; #define rep(i,x,y) for(int i=x;i<y;i++) #define rept(i,x,y) for(int i=x;i<=y;i++) #define per(i,x,y) for(int i=x;i>=y;i--) #define all(x) x.begin(),x.end() #define pb push_back #define fi first #define se second #define mes(a,b) memset(a,b,sizeof a) #define mp make_pair #define dd(x) cout<<#x<<"="<<x<<" " #define de(x) cout<<#x<<"="<<x<<" " #define debug() cout<<"I love Miyamizu Mitsuha forever. " const int inf=0x3f3f3f3f; const int maxn=105; class matrix { public: ull arrcy[35][35]; int row,column; matrix() { memset(arrcy,0,sizeof arrcy); column=row=0; } friend matrix operator *(matrix s1,matrix s2) { int i,j; matrix s3; for (i=0;i<s1.row;i++) { for (j=0;j<s2.column;j++) { for (int k=0;k<s1.column;k++) { s3.arrcy[i][j]+=s1.arrcy[i][k]*s2.arrcy[k][j]; } } } s3.row=s1.row; s3.column=s2.column; return s3; } void show() { for(int i=0;i<row;i++) { for (int j=0;j<column;j++) cout<<arrcy[i][j]<<" "; cout<<endl; } } }mat,mul; matrix quick_pow(matrix s1,long long n) { matrix mul=s1,ans; ans.row=ans.column=s1.row; memset(ans.arrcy,0,sizeof ans.arrcy); for(int i=0;i<ans.row;i++) ans.arrcy[i][i]=1; while(n) { if(n&1) ans=ans*mul; mul=mul*mul; n>>=1; } return ans; } ull qpow(ull a,ull b) { ull ans=1; for(;b;b>>=1,a=a*a) if(b&1) ans*=a; return ans; } class Trie { public: Trie() { cnt=1; } int cnt; int trie[maxn][26]; int fail[maxn]; bool bad[maxn]; void init() { rep(i,0,cnt) { fail[i]=bad[i]=0; rep(j,0,26) trie[i][j]=0; } cnt=1; } void insert(string s) { int len=s.size(); int pos=0; rep(i,0,len) { int next=s[i]-'a'; if(!trie[pos][next]) trie[pos][next]=cnt++; pos=trie[pos][next]; } bad[pos]=1; } void getfail() { queue<int> q; rep(i,0,26) { if(trie[0][i]) { fail[trie[0][i]]=0; q.push(trie[0][i]); } } while(!q.empty()) { int pos=q.front(); q.pop(); rep(i,0,26) { bad[pos]|=bad[fail[pos]]; if(trie[pos][i]) { fail[trie[pos][i]]=trie[fail[pos]][i]; q.push(trie[pos][i]); } else trie[pos][i]=trie[fail[pos]][i]; } } } }ac; string s; ull cal(ll x) { matrix l,r; r.row=2;r.column=1; r.arrcy[0][0]=0; r.arrcy[1][0]=1; l.row=l.column=2; l.arrcy[0][0]=l.arrcy[0][1]=26; l.arrcy[1][0]=0;l.arrcy[1][1]=1; r=quick_pow(l,x)*r; return r.arrcy[0][0]; } int main() { ios::sync_with_stdio(false); cin.tie(0); int n,l; while(cin>>n>>l) { ac.init(); mes(mul.arrcy,0);mes(mat.arrcy,0); rep(i,0,n) { cin>>s; ac.insert(s); } ac.getfail(); mat.row=1; mat.column=ac.cnt+1; mat.arrcy[0][0]=1; mul.row=mul.column=ac.cnt+1; rep(i,0,ac.cnt) { if(ac.bad[i]) continue; rep(j,0,26) { if(!ac.bad[ac.trie[i][j]]) mul.arrcy[i][ac.trie[i][j]]++; } } rep(i,0,mul.column) mul.arrcy[i][mul.column-1]=1; mat=mat*quick_pow(mul,l); ull ans=0; rep(i,0,mat.column) ans=(ans+mat.arrcy[0][i]); ans--; ull cnt=cal(l); cnt-=ans; cout<<cnt<<" "; } return 0; }