洛谷P1357 花園(狀態壓縮 + 矩陣快速冪加速遞推)
阿新 • • 發佈:2018-11-14
題目連結:傳送門
題目:
題目描述 小L有一座環形花園,沿花園的順時針方向,他把各個花圃編號為1~N(2<=N<=10^15)。他的環形花園每天都會換一個新花樣,但他的花園都不外乎一個規則,任意相鄰M(2<=M<=5,M<=N)個花圃中有不超過K(1<=K<M)個C形的花圃,其餘花圃均為P形的花圃。 例如,N=10,M=5,K=3。則 CCPCPPPPCC 是一種不符合規則的花圃; CCPPPPCPCP 是一種符合規則的花圃。 請幫小L求出符合規則的花園種數Mod 1000000007 由於請您編寫一個程式解決此題。 輸入輸出格式 輸入格式: 一行,三個數N,M,K。 輸出格式: 花園種數ModView Code1000000007 輸入輸出樣例 輸入樣例#1: 10 5 3 輸出樣例#1: 458 輸入樣例#2: 6 2 1 輸出樣例#2: 18 說明 【資料規模】 40%的資料中,N<=20; 60%的資料中,M=2; 80%的資料中,N<=10^5。 100%的資料中,N<=10^15。
思路:
乍一看是一個環形dp:
對於給定的長度為M的狀態,其後面長度為M-1的部分會影響下一個狀態,記一個cnt1表示已經放了C型花圃的數量,那麼根據當前的cnt1是否小於K,可以決定下個狀態的轉移。
狀態:
f[i][j]:以第i個位置為終點的長度為M的部分,狀態為j的方案數。(j是用2進位制狀壓的一個長度為M的狀態)
狀態轉移方程:
f[i][j] += f[i-1][j>>1];
if (count1(j) == K && !(j&1))
f[i][j] += f[i-1][(j>>1)|(1<<M)];
。。。。。。
但是N的上限高達1e15,所以常規的方法沒發跑,考慮用矩陣加速。
那就要找狀態矩陣和轉移矩陣了。
狀態矩陣:
Fn = [f[n][0], f[n][1], ... , f[n][(1<<m)-1];
轉移矩陣可以通過上面的狀態轉移方程,用dfs預處理 出來,接下來就可以用快速冪加速了。
因為跑N次之後和不跑的時候狀態相同(環形),所以直接求轉移矩陣A的N次AN的對角線上的和即可。
程式碼:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MOD = 1e9 + 7; ll N, M, K; int bin[6]; ll F[32][32], A[32][32]; void addTran(int sta, int cnt1) { int pre = sta >> 1; A[pre][sta] = 1; if (cnt1 == K && !(sta&1)) return; A[pre|bin[M-1]][sta] = 1; } void dfs(int dep, int sta, int cnt1) { if (dep == M) { addTran(sta, cnt1); return; } dfs(dep+1, sta, cnt1);//dep+1位為0 if (cnt1 < K && dep+1 < M) dfs(dep+1, sta|bin[dep+1], cnt1+1);//dep+1位為1 } void mul(ll f[32][32], ll a[32][32]) { ll c[32][32]; memset(c, 0, sizeof c); for (int i = 0; i < 32; i++) for (int j = 0; j < 32; j++) for (int k = 0; k < 32; k++) c[i][j] = (c[i][j] + f[i][k] * a[k][j]) % MOD; memcpy(f, c, sizeof c); } int main() { bin[0] = 1; for (int i = 1; i <= 5; i++) bin[i] = bin[i-1] << 1; cin >> N >> M >> K; memset(A, 0, sizeof A); memset(F, 0, sizeof F);//所有長度為M的狀態 for (int i = 0; i < bin[M]; i++) F[i][i] = 1; dfs(0, 0, 0);//初始化轉移矩陣 dfs(0, 1, 1); for (; N; N >>= 1) { if (N&1) mul(F, A); mul(A, A); } ll ans = 0; for (int i = 0; i < bin[M]; i++) ans = (ans + F[i][i]) % MOD; cout << ans << endl; return 0; } /* 6 2 1 */View Code