• 洛谷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
  • 相关阅读:
    STM32时钟树
    js jQuery函数 $.ajax()
    jQuery 语法
    jQuery介绍
    python笔记2 生成器 文件读写
    python笔记1,语法,函数,类和实例,异常
    Scrapy爬虫入门系列4抓取豆瓣Top250电影数据
    Scrapy爬虫入门系列3 将抓取到的数据存入数据库与验证数据有效性
    opus 规范 与参数解析
    开源播放器ijkplayer源码结构
  • 原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/9958814.html
Copyright © 2020-2023  润新知