\(\text{Description}\)
\(\text{Solution}\)
可以看看这道题的前置版本:\(\text{POJ - 2778 DNA Sequence}\),这样会容易理解一点。
容易发现其实方案数就是所有长度小于等于 \(m\) 的方案数 \(-\) 长度小于等于 \(m\) 的不包含任何关键串的方案数。
考虑第一项其实就是 \(26^1+26^2+...+26^n\)。
这个可以用等比数列求和或者用矩阵加速来递推。
讲一下矩阵加速。令 \(f_n=1+26^1+26^2+...+26^n\)。
显然有:
\[f_n=f_{n-1}\times 26+1
\]
最后求出 \(f_n\) 再 \(-1\) 就是第一项。
对于第二项,由于我们需要将矩阵每次转移的 \([1,m]\) 的状态的第一行之和再相加,可以在矩阵后面添置一维。
比如原矩阵为:
\[\left[
\begin{matrix}
1 & 2 \\
3 & 4 \\
\end{matrix}
\right]
\]
我们将最后一列置为 \(1\),最后一行除了最后一列的那个置为 \(0\)。
\[\left[
\begin{matrix}
1 & 2 & 1 \\
3 & 4 & 1 \\
0 & 0 & 1
\end{matrix}
\right]
\]
你会发现这样无论矩阵怎么乘最后一行是不变的,最后一列除了最后一行的那个数都是上一轮的同行的数相加,比如我们计算上面那个矩阵的平方时,\((0,2)\) 的答案就是 \(1+2+1\)。而且保证原矩阵范围未受影响。
注意这样算出来的答案要减去初始赋值的 \(1\)。
\(\text{Code}\)
#include <cstdio>
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
#include <queue>
#include <cstring>
using namespace std;
typedef unsigned long long ll;
queue <int> q;
ll ans,sum;
int n,m,t[35][30],f[35],tot;
bool no[35];
char s[10];
struct Matrix {
ll a[35][35]; int n,m;
Matrix() {memset(a,0,sizeof a);}
Matrix operator * (Matrix t) {
Matrix r; r.n=n,r.m=m;
rep(i,0,r.n)
rep(j,0,r.m)
if(a[i][j])
rep(k,0,r.m)
r.a[i][k]=(r.a[i][k]+a[i][j]*t.a[j][k]);
return r;
}
} per;
Matrix qkpow(int y) {
Matrix r; r.n=r.m=per.n;
rep(i,0,r.n) r.a[i][i]=1;
while(y) {
if(y&1) r=r*per;
per=per*per; y>>=1;
}
return r;
}
void Insert() {
int p=0,len=strlen(s+1);
rep(i,1,len) {
int d=s[i]-'a';
if(!t[p][d]) t[p][d]=++tot;
p=t[p][d];
}
no[p]=1;
}
void GetFail() {
rep(i,0,25) if(t[0][i]) q.push(t[0][i]);
while(!q.empty()) {
int u=q.front(); q.pop();
rep(i,0,25)
if(t[u][i]) f[t[u][i]]=t[f[u]][i],q.push(t[u][i]),no[t[u][i]]|=no[f[t[u][i]]];
else t[u][i]=t[f[u]][i];
}
}
void Ori() {
sum=ans=0; tot=0; per=Matrix();
memset(t,0,sizeof t);
memset(no,0,sizeof no);
memset(f,0,sizeof f);
}
void init() {
per=Matrix();
rep(i,0,tot)
if(!no[i])
rep(j,0,25)
if(!no[t[i][j]]) ++per.a[i][t[i][j]];
per.n=per.m=tot+1;
rep(i,0,per.m) per.a[i][per.m]=1;
}
int main() {
while(~scanf("%d %d",&n,&m)) {
Ori();
while(n--) scanf("%s",s+1),Insert();
GetFail(); init();
per=qkpow(m);
rep(i,0,per.m) ans+=per.a[0][i];
per=Matrix(); per.n=per.m=1;
per.a[0][0]=26,per.a[1][0]=per.a[1][1]=1;
per=qkpow(m);
rep(i,0,1) sum+=per.a[i][0];
print(sum-ans,'\n'); // sum 与 ans 都多计算了一个 1,所以直接相减就是答案
}
return 0;
}
\(\text{Bi Bi Time!}\)
等比数列求和推出来答案柿子是:
\[\frac{26^n\times 26-26}{25}
\]
然后我跑去求 \(25\) 在 \(2^{64}\) 意义下的逆元,求出来这个东西:\(15170602326218735249\).
但为什么乘出来不是 \(1\)?按理来说这两个玩意儿互质不是会有逆元吗?
\(\text{Update on 2022.2.17}\):
我是 \(\rm sb\),用 exgcd()
+ \(\text{__int128}\) 可以算出来逆元是这个东西:\(10330176681277348905\).