1. 程式人生 > >HDU 5863 cjj's string game ( 16年多校10 G 題、矩陣快速冪優化線性遞推DP )

HDU 5863 cjj's string game ( 16年多校10 G 題、矩陣快速冪優化線性遞推DP )

sca 組合數 矩陣 spl blank mage acm 組合 str

題目鏈接

題意 : 有種不同的字符,每種字符有無限個,要求用這k種字符構造兩個長度為n的字符串a和b,使得a串和b串的最長公共部分長度恰為m,問方案數

分析 :

直覺是DP

不過當時看到 n 很大、但是 m 很小的時候

發現此題DP並不合適、於是想可能是某種組合數學的問題可以直接公式算

看到題解的我、恍然大悟、對於這種數據、可以考慮一下矩陣快速冪優化的DP

首先要想到線性遞推的 DP 式子

最直觀的想法就是 dp[i][j] = 到第 i 個位置為止、前面最長匹配長度為 j 的方案數

但是如果仔細想想、這樣子的定義狀態並不好轉移、遂換一種思路

定義 dp[i][j] = 到第 i 個位置為止、以第 i 個字符為結尾的匹配串的長度為 j 的方案數

有轉移

dp[i][0] = (dp[i-1][0] + dp[i-1][1] + .... + dp[i-1][m] ) * k * (k-1) (k * (k-1) 的意義是a、b串第 i 個字符不一樣的方案數)

dp[i][j] = dp[i-1][j-1] * k ( j ≤ i )

然後嘗試去構造矩陣、此處引用 鏈接

技術分享圖片

但是註意一下這裏的 DP 意義、答案最後並不是 dp[n][m]

dp[n][0] + dp[n][1] + ... + dp[n][m] 可以看成到第 n 個位置為止匹配長度 ≤ m 的方案數

那麽如果可以得到匹配長度 ≤ m-1 的方案數兩者相減就可以得到匹配長度恰為 m 的方案數了

所以做兩次矩陣快速冪即可

技術分享圖片
#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned long long

#define scl(i) scanf("%lld", &i)
#define scll(i, j) scanf("%lld %lld", &i, &j)
#define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
#define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l)

#define
scs(i) scanf("%s", i) #define sci(i) scanf("%d", &i) #define scd(i) scanf("%lf", &i) #define scIl(i) scanf("%I64d", &i) #define scii(i, j) scanf("%d %d", &i, &j) #define scdd(i, j) scanf("%lf %lf", &i, &j) #define scIll(i, j) scanf("%I64d %I64d", &i, &j) #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k) #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k) #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k) #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l) #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l) #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l) #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define lowbit(i) (i & (-i)) #define mem(i, j) memset(i, j, sizeof(i)) #define fir first #define sec second #define VI vector<int> #define ins(i) insert(i) #define pb(i) push_back(i) #define pii pair<int, int> #define VL vector<long long> #define mk(i, j) make_pair(i, j) #define all(i) i.begin(), i.end() #define pll pair<long long, long long> #define _TIME 0 #define _INPUT 0 #define _OUTPUT 0 clock_t START, END; void __stTIME(); void __enTIME(); void __IOPUT(); using namespace std; const int maxn = 1e5 + 10; const LL mod = 1e9 + 7; struct MAT{ LL val[12][12]; int sz; MAT(){}; MAT(int _sz){ sz = _sz; memset(val, 0, sizeof(val)); } friend MAT operator * (const MAT & A, const MAT & B){ MAT C(A.sz); for(int k = 1; k <= C.sz; k++) for(int i = 1; i <= C.sz; i++){ if(A.val[i][k] == 0) continue; for(int j = 1; j <= C.sz; j++){ C.val[i][j] = C.val[i][j] + A.val[i][k] * B.val[k][j] % mod; if(C.val[i][j] >= mod) C.val[i][j] -= mod; } } return C; } }; MAT pow_mod(MAT a, LL b) { MAT ret(a.sz); for(int i=1; i<=ret.sz; i++) ret.val[i][i] = 1; while(b){ if(b & 1) ret = ret * a; a = a * a; b >>= 1; }return ret; } LL Cal(int n, int m, int k) { MAT A(m+1); for(int i=1; i<=A.sz; i++) A.val[1][i] = 1LL * k * (k - 1); for(int i=2; i<=A.sz; i++) A.val[i][i-1] = k * 1LL; A = pow_mod(A, n); LL ret = 0; for(int i=1; i<=A.sz; i++) ret = (ret + A.val[i][1]) % mod; return ret; } int main(void){__stTIME();__IOPUT(); int nCase; sci(nCase); while(nCase--){ int n, m, k; sciii(n, m, k); printf("%lld\n", (Cal(n, m, k) - Cal(n, m-1, k) + mod) % mod); } __enTIME();return 0;} void __stTIME() { #if _TIME START = clock(); #endif } void __enTIME() { #if _TIME END = clock(); cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl; #endif } void __IOPUT() { #if _INPUT freopen("in.txt", "r", stdin); #endif #if _OUTPUT freopen("out.txt", "w", stdout); #endif }
View Code

HDU 5863 cjj's string game ( 16年多校10 G 題、矩陣快速冪優化線性遞推DP )