luogu1357 花园 状态压缩 矩阵快速幂

题目大意

  小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(2<=N<=10^15)。他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻M(2<=M<=5,M<=N)个花圃中有不超过K(1<=K<M)个C形的花圃,其余花圃均为P形的花圃。请帮小L求出符合规则的花园种数Mod 1000000007。

题解

状态的设计

  我们先不考虑环。我对状态的设计是f(i, j, k),i是以第i位起始,j是区间[i, i+m]中最后一个C的位置-i的值,k是[i, i+m]中C的数量,f是排列的种数。后来我认为j也不需要,f(i, k)就行了。但是此方法一个k无法表示出具体的排布状态,这是错误的。

  我们看到M<=5,很容易想到用状态压缩来表示具体的状态。所以我们设计出f(i, S),i是位置,S是[i, i+M]中C的排布状态,f是排列的个数。递推式f(i + 1, S >> 1) += f(i, S), f(i + 1, (S >> 1) | (1 << M - 1) += f(i, S),其中涉及到的所有集合内元素个数都不超过K。

环的处理

  我当时想到将长度为n的序列尾部再加一个长度为m的序列,从左往右递推,最后输出的结果便是sum{f(n, S)},S满足元素个数<=K,但考虑到这样会导致结尾m个花的排列状态和开头m个花的排列状态不一致而导致错误。于是我就卡住了。

  正确的解决办法是枚举开头[1, m]的花的状态S,每次将其固定住,这样DP推导出的DP(n, S)都是由[m + 1, n + m]处花合法的排列得出的了。

矩阵快速幂优化

  我们看到对于每一个状态S0,无论i是多少,可以使f(i, S0)去+=的出处f(i+1, S0')都是固定的。所以我们建立一个矩阵A,A(S1, S2)=[对任意i, f(i + 1, S2)需要+=f(i, S1)]。[]内判断为真即1,否则为0。这样,对于每一个开头[1, m]的花的状态S0,建立一个向量B,其中只有S0那一位的项为1(排列方式由S0知道肯定为1),最终的结果便是B*A^N。最后求和即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
using namespace std;

#define ll long long
const int MAX_M = 7;
const ll P = 1000000007;
ll N, M, K;

struct Matrix
{
private:
    int TotRow, TotCol;

public:
    ll A[1 << MAX_M][1 << MAX_M];

    void Clear()
    {
        memset(A, 0, sizeof(A));
    }

    Matrix(int totRow, int totCol) :TotRow(totRow), TotCol(totCol) { Clear(); }

    Matrix operator * (const Matrix& a) const
    {
        assert(TotCol == a.TotRow);
        Matrix ans(TotRow, a.TotCol);
        for (int row = 0; row < TotRow; row++)
            for (int col = 0; col < a.TotCol; col++)
                for (int k = 0; k < TotCol; k++)
                    ans.A[row][col] = (ans.A[row][col] + (A[row][k] * a.A[k][col] % P)) % P;
        return ans;
    }

    Matrix Power(ll n)
    {
        Matrix ans(TotRow, TotCol);
        for (int i = 0; i < TotRow; i++)
            ans.A[i][i] = 1;
        Matrix a = *this;
        while (n)
        {
            if (n & 1)
                ans = ans * a;
            a = a * a;
            n >>= 1;
        }
        return ans;
    }
};


int main()
{
    scanf("%lld%lld%lld", &N, &M, &K);

    static bool Exist[1 << MAX_M];
    for (int S = 0; S < (1 << M); S++)
    {
        int cnt = 0;
        for (int i = 0; i < M; i++)
            cnt += (S & (1 << i)) > 0;
        Exist[S] = cnt <= K;
    }

    static Matrix g(1 << M, 1 << M);
    for (int S = 0; S < (1 << M); S++)
    {
        if (Exist[S])
        {
            g.A[S][S >> 1] = 1;
            if (Exist[(S >> 1) | (1 << M - 1)])
                g.A[S][(S >> 1) | (1 << M - 1)] = 1;
        }
    }

    static Matrix powed = g.Power(N), start(1, 1 << M);
    ll ans = 0;
    for (int S = 0; S < (1 << M); S++)
    {
        if (Exist[S])
        {
            start.Clear();
            start.A[0][S] = 1;
            ans = (ans + (start * powed).A[0][S]) % P;
        }
    }
    printf("%lld
", ans);
}

  

原文地址:https://www.cnblogs.com/headboy2002/p/9519712.html