题目大意

  小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\n", ans);
}

  

luogu1357 花园 状态压缩 矩阵快速幂的更多相关文章

  1. 洛谷P1357 花园(状态压缩 + 矩阵快速幂加速递推)

    题目链接:传送门 题目: 题目描述 小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(<=N<=^).他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻 ...

  2. P1357 花园 状压 矩阵快速幂

    题意 小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(2<=N<=10^15).他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻M(2<=M& ...

  3. hihocoder第42周 3*N骨牌覆盖(状态dp+矩阵快速幂)

    http://hihocoder.com/contest/hiho42/problem/1 给定一个n,问我们3*n的矩阵有多少种覆盖的方法 第41周做的骨牌覆盖是2*n的,状态转移方程是dp[i] ...

  4. hihocoder第42周 k*N骨牌覆盖(状态dp+矩阵快速幂)

    上周的3*N的骨牌,因为状态只有8中,所以我们可以手算出状态转移的矩阵 但是这周是k*N,状态矩阵不好手算,都是我们改成用程序自动生成一个状态转移的矩阵就行了,然后用这个矩阵进行快速幂即可 枚举枚举上 ...

  5. 【BZOJ2004】公交线路(动态规划,状态压缩,矩阵快速幂)

    [BZOJ2004]公交线路(动态规划,状态压缩,矩阵快速幂) 题面 BZOJ 题解 看到\(k,p\)这么小 不难想到状态压缩 看到\(n\)这么大,不难想到矩阵快速幂 那么,我们来考虑朴素的\(d ...

  6. [luogu1357] 花园 [dp+矩阵快速幂]

    题面: 传送门 思路: 把P形花圃记录为0,C形记录为1,那么一段花圃就可以状态压缩成一个整数 那么,我们可以有这样的状压dp: dp[i][S]表示前i个花圃,最后m个的状态为S的情况 如果这是一条 ...

  7. P1357 花园 (矩阵快速幂+ DP)

    题意:一个只含字母C和P的环形串 求长度为n且每m个连续字符不含有超过k个C的方案数 m <= 5  n <= 1e15 题解:用一个m位二进制表示状态 转移很好想 但是这个题是用矩阵快速 ...

  8. bzoj2004 矩阵快速幂优化状压dp

    https://www.lydsy.com/JudgeOnline/problem.php?id=2004 以前只会状压dp和矩阵快速幂dp,没想到一道题还能组合起来一起用,算法竞赛真是奥妙重重 小Z ...

  9. ZZNU 2182 矩阵dp (矩阵快速幂+递推式 || 杜教BM)

    题目链接:http://47.93.249.116/problem.php?id=2182 题目描述 河神喜欢吃零食,有三种最喜欢的零食,鱼干,猪肉脯,巧克力.他每小时会选择一种吃一包. 不幸的是,医 ...

随机推荐

  1. javascript特殊值常量

    Infinity 表示无穷大的特殊值.当数字运算结果超出javascript能表示的数字范围时. Nan 特殊的非数字值(not a number).0除0.数字运算符的操作数为字符等情况. Numb ...

  2. MySQL索引----数据结构及算法原理

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...

  3. python算数运算符

    ---恢复内容开始--- 加减乘除 >>> 1+1 2 >>> 4-2 2 >>> 2*5 10 >>> 8/2 4.0 > ...

  4. sublime text3配置插件

    之前一直习惯用记事本写代码,懒得用IDE,虽然知道用 IDE效率高一些,不过觉得还是用记事本纯手写代码,比较容易记忆.直到昨天写代码遇到了点问题,截图给师兄看,师兄就问我是不是用记事本写代码,为什么不 ...

  5. android接收mjpg-streamer软件视频流

    [代码]主要实现代码 package cn.dong.mjpeg; import java.io.InputStream; import java.net.HttpURLConnection; imp ...

  6. MyBatis入门3_mapper.xml优化(parameterType简写_NameSpace简写_sql片段_特殊字符处理)_动态SQL

    本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! 优化 1.起别名(一般不用,写全方便查看类出处) 以前 ...

  7. eclipse中代码整体左右移动的方法

    1.向左:将要移动的代码选中,然后按TAB键2.向右:将要移动的代码选中,然后按shift+tab键 kettas:  2009-8-21

  8. jenkins环境搭建(Windows)

    1.下载并解压Tomcat Tomcat官方网站:http://tomcat.apache.org/ 下载并解压,解压后的目录结构如下: 2.下载并安装适合自己电脑系统的 jenkins Jenkin ...

  9. ES6 作用域的问题

  10. cstringlist不完全用法

    CStringList是CString链表,在MFC编程中STL之外的另一选择,用起来更加简洁. 插入数据:AddTail();AddHead() 删除数据:RemoveAll();RemoveAt( ...