HDU 5863 cjj's string game ( 16年多校10 G 题矩阵快速幂优化线性递推DP )
Posted rubbishes
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 5863 cjj's string game ( 16年多校10 G 题矩阵快速幂优化线性递推DP )相关的知识,希望对你有一定的参考价值。
题意 : 有种不同的字符,每种字符有无限个,要求用这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 ", (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 }
以上是关于HDU 5863 cjj's string game ( 16年多校10 G 题矩阵快速幂优化线性递推DP )的主要内容,如果未能解决你的问题,请参考以下文章
HDU - 3973 AC's String(Hash+线段树)