\(\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\).

posted on 2020-12-12 23:59  Oxide  阅读(93)  评论(0编辑  收藏  举报