P1357 花园

P1357 花园 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

为了方便理解,数学公式不是完全的数学。

m 很小,相邻 m 个花圃的情况也就 \(2^m = 32\) 种,可以状态压缩(0为p,1为c),对于一个状态 S = 1010,可以看做是由 X101 加上0(X1010)后,去掉 X 推过来的,可以认为整个花圃就是这样一个接一个推出来的,那么就要用 DP 统计,链。设 \(f[i][s]\) 为有 i 个花圃且最后 m 个状态为 s,那么很容易得到递推式子:

\[f[i][s] = f[i - 1][s >> 1] + f[i - 1][s >> 1 | (1 << m - 1)] \]

其中 \(s >> 1 | (1 << m - 1)\)\(s >> 1\) 分别是 X 为 1 和为 0 状态,自己容易求出。

而花圃是环,要破环为链,正常复制原序列不好统计,于是转换思路(这里是变环为链),一个有n个点的环与一个有n+m个点且前m个点与后m个点完全相同的链的种类数是一样的。这 m 个点就可以用我们的状态 S。而这些状态 S 不能同时作为初始状态即:\(f[m][s] = 1\) 否则最后得到的 \(f[n + m][s]\) 无法分出前后 m 个是否相同。所以要单独统计,而前 m 个数包含所有合法 S,所以每个都要统计,算入总和即为答案。

分别枚举每个初始合法状态 S,以 \(f[m][s] = 1\) 为递推初值,其余(包括其他 \(f[m][s]\)\(f\)\(0\),那么最后得出 \(f[n + m][s]\) 就是前面 m 个点和后面 m 个点相同的序列的种类数的一部分。

因为 n 很大,为了加速要使用矩阵快速幂,加快递推(也因为递推方程简单)。建议用 dfs 写转移矩阵。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

#define x first
#define y second

using namespace std;

typedef long long LL; // 记得开longlong
typedef pair<int, int> PII;

const int M = (1 << 5) + 2, mod = 1e9 + 7;

LL n, m, k;
PII id[M];
int sum, cnt;
int st[M], g[M];

struct Mat
{
    int l, r;
    int a[M][M];
}A, B; // 初始矩阵,转移矩阵

void print(Mat &A) // 用来输出矩阵,方便调试
{
    for (int i = 1; i <= A.l; i ++ )
    {
        for (int j = 1 ; j <= A.r; j ++ )
            printf("%d ", A.a[i][j]);
        puts("");
    }
    puts("");
}


int get(int x)
{
    int cnt = 0;
    while (x)
    {
        if (x & 1) cnt ++ ;
        x >>= 1;
    }
    return cnt;
}

void dfs(int x)
{
    if (x > cnt) return;
    
    int i = g[x];
    int a = id[i].x, b = id[i].y;
    
    if (st[a]) B.a[st[a]][x] = 1;
    if (st[b]) B.a[st[b]][x] = 1;
    dfs(x + 1);
}

Mat Mat_mul(Mat &A, Mat &B, int p) // 矩阵乘
{
    Mat C;
    C = {A.r, A.r};

    for (int i = 1; i <= A.l; i ++ )
        for (int j = 1; j <= B.r; j ++ )
            for (int k = 1; k <= A.r; k ++ )
                C.a[i][j] = (C.a[i][j] + 1ll * A.a[i][k] * B.a[k][j] % mod) % mod;
    // print(C);   
    return C;
}

Mat Mat_qmi(Mat A, LL k, int p) // 矩阵快速幂
{
    Mat res = {A.l, A.r};
    for (int i = 1; i <= A.l; i ++ ) res.a[i][i] = 1;
    // print(res);
    while (k)
    {
        if (k & 1) res = Mat_mul(res, A, p);
        k >>= 1;
        A = Mat_mul(A, A, p);
        // print(res);
    }
    
    return res;
}


int main()
{
    cin >> n >> m >> k;
    
    
    for (int i = 0; i < (1 << m); i ++ )
    {
        id[i] = {i >> 1, i >> 1 | (1 << m - 1)};
        if (get(i) <= k) 
        {
            st[i] = ++ cnt;
            g[cnt] = i;
            // printf("%d %d %d %d\n", id[i].x, id[i].y, i, cnt);
        }
    }
    A.l = 1;
    A.r = cnt;
    
    B.l = B.r = cnt;
    dfs(1); 
    B = Mat_qmi(B, n, mod);
    int ans = 0;
    for (int i = 0; i < (1 << m); i ++ )
    {
        if (st[i] == 0) continue;
        Mat C = A;
        C.a[1][st[i]] = 1;
        
        Mat x = Mat_mul(C, B, mod);
        ans = (ans + x.a[1][st[i]]) % mod;
    }
    
    
    cout << ans << endl;
    
    return 0;
}
posted @ 2024-10-12 15:34  blind5883  阅读(2)  评论(0编辑  收藏  举报