zoukankan      html  css  js  c++  java
  • 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);
    }
    

      

  • 相关阅读:
    iaure学习网站
    linux下环境搭建比较
    微信分享jsdk接口
    微信接口开发遇到的问题
    Centos7.6部署k8s(v1.14.2)集群
    k8s简介
    nginx配置ssl证书
    kafka zookeeper介绍
    mysql数据库的备份与还原
    centos7 部署jumpserver
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9519712.html
Copyright © 2011-2022 走看看