洛谷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

猜你喜欢

转载自www.cnblogs.com/Lubixiaosi-Zhaocao/p/9958814.html