1. 程式人生 > >洛谷P1357 花園(狀態壓縮 + 矩陣快速冪加速遞推)

洛谷P1357 花園(狀態壓縮 + 矩陣快速冪加速遞推)

題目連結:傳送門

題目:

題目描述

小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。

輸出格式:

花園種數Mod 
1000000007 輸入輸出樣例 輸入樣例#110 5 3 輸出樣例#1458 輸入樣例#26 2 1 輸出樣例#218 說明 【資料規模】 40%的資料中,N<=2060%的資料中,M=280%的資料中,N<=10^5100%的資料中,N<=10^15
View Code

思路:

  乍一看是一個環形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