1. 程式人生 > 其它 >題解 [HNOI2008]GT考試

題解 [HNOI2008]GT考試

傳送門

這題暴力對拍都難搞,差評

一般的題解裡思路是考慮一般DP:
\(dp[i][j]\)為列舉到第i位時匹配到第j位的方案數,令\(g[k][j]\)為將匹配到k位的情況補到匹配到j位的方案數

\[dp[i][j] = \sum\limits_{k=0}^{m-1}dp[i-1][k]*g[k][j] \]

然後這個式子就是矩陣快速冪的形式了

然而我麻煩億點的做法:
\(dp[i]\)為長度為i時的合法方案數,輔助陣列\(k[i]\)為鎖定前\(m\)位為匹配串唯一一次出現時的方案數
考慮轉移,則轉移為\(dp[i]=dp[i-m]*(10^m-1)\)減去所有跨越\(i-m\)這個邊界的不合法方案數

拿以下輸入舉例子

6 3 1000000
121

這裡有幾種可能的轉移:

121000
×121??
××121?

對於第2行,×有10種可能,??有99種可能(有一種不滿足前m位為匹配串唯一一次出現),共990種
對於第3行,××有99種可能(有一種開頭是121的已在轉移時減掉了,再減就重了),?有10種可能,共990種
所以\(dp[6] = dp[3]*999-990-990 = 996021\)
k陣列的轉移和dp陣列類似,懶得寫了不再贅述

時間複雜度\(O((m*2+2)^3logn)\)也就慢了那麼億點點

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 45
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

int n, m, mod;
int nxt[N], power[N];
bool vis[N];
char s[N];

struct matrix{
	int n, m;
	int a[N][N];
	matrix(){n=m=0; memset(a, 0, sizeof(a));}
	matrix(int n_, int m_):n(n_),m(m_) {memset(a, 0, sizeof(a));}
	inline void resize(int n_, int m_) {n=n_; m=m_;}
	inline void put() {for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) cout<<setw(5)<<a[i][j]<<' '; cout<<endl;}}
	inline int* operator [] (int i) {return a[i];}
	inline matrix operator * (matrix b) {
		matrix c(n, b.m);
		for (int i=1; i<=n; ++i) 
			for (int k=1; k<=m; ++k) {
				if (!a[i][k]) continue;
				for (int j=1; j<=b.m; ++j) 
					c[i][j]=(c[i][j]+a[i][k]*b[k][j]%mod)%mod;
			}
		return c;
	}
}ans, t;

matrix qpow(matrix a, int b) {
	if (!b) return a;
	matrix ans=a; --b;
	while (b) {
		if (b&1) ans=ans*a;
		a=a*a; b>>=1;
	}
	return ans;
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	scanf("%d%d%d%s", &n, &m, &mod, s+1);
	nxt[1]=0;
	power[0]=1;
	for (int i=1; i<=m; ++i) power[i]=power[i-1]*10%mod;
	//for (int i=1; i<=m; ++i) cout<<power[i]<<' '; cout<<endl;
	int p=(power[m]-1)%mod;
	//cout<<p<<endl;
	for (int i=2,j=0; i<=m; ++i) {
		while (j && s[i]!=s[j+1]) j=nxt[j];
		if (s[i]==s[j+1]) ++j;
		nxt[i] = j;
	}
	for (int i=nxt[m]; i; i=nxt[i]) vis[i]=1;
	ans.resize(1, m*2+2);
	t.resize(m*2+2, m*2+2);
	ans[1][1]=-1; ans[1][2]=1;
	for (int i=1; i<=m; ++i) ans[1][2+i]=ans[1][1+i]*10%mod;
	--ans[1][m+2];
	//ans[1][m*2+1]=1; ans[1][m*2+2]=ans[1][m*2+1]*10-(nxt[m]==m-1);
	ans[1][m*2+2]=1;
	
	t[1][1]=1;
	for (int i=1; i<=m; ++i) t[2+i][1+i]=1;
	t[3][m+2]=p;
	//for (int i=nxt[m]; i; i=nxt[i]) t[m+2+i][m+2]=-1;
	for (int i=1; i<m; ++i) t[2*m+3-i][m+2]=-(power[i]-vis[m-i])%mod;
	//for (int i=1; i<m; ++i) t[2*m+2-i][m+2]=-1;
	for (int i=1; i<m; ++i) t[m+3+i][m+2+i]=1;
	//t[m*2+2][m*2+2]=10;
	//if (nxt[m]) t[1][m+2+nxt[m]+1]=1;
	for (int i=nxt[m]; i; i=nxt[i]) t[m+3+i][m*2+2]=-1;
	t[3][m*2+2]=1;
	//t[m*2+2][m+2]=-1;
	
	#ifdef DEBUG
	ans.put(); cout<<endl;
	for (int i=1; i<=n-m; ++i) {
		ans=ans*t;
		ans.put(); cout<<endl;
	}
	t.put(); cout<<endl;
	#else
	if (n-m) ans=ans*qpow(t, n-m);
	#endif
	printf("%d\n", ((ans[1][m+2])%mod+mod)%mod);
	
	return 0;
}