题目类型:状压\(DP\) -> 矩阵乘法

绝妙然而思维难度极其大的一道好题!

传送门:>Here<

题意:有一个环形花圃,可以种两种花:0或1. 要求任意相邻的\(M\)个花中1的个数不超过\(k\)个。总共有\(N\)个花。问方案数

解题思路

非常巧妙的一道题。

先看如何拿到\(80pts\)

\(N \leq 10^5\),也就是说可以\(O(n)\)带若干常数。我们发现影响当前状态的决策的仅仅就是离它最近的那\(M\)个花圃。由此可以进行状压\(DP\),\(dp[i][s]\)表示目前决策第\(i\)个花圃,并且左侧\(M\)个花圃的状态为\(s\)时的方案数。很明显可以通过\(i-1\)时的状态来进行转移。由于仅仅只是向右移动了一格,所以原先的右边\(M-1\)个不动,新加进来的那个最右侧的可以是1或者0。当然在写方程的时候是要倒过来的,于是我们可以得到方程$$dp[i][s] = dp[i-1][(s/2)+2^{M-1}] + dp[i-1][s/2]$$我们可以先\(dfs\)预处理出所有的可能状态。

那么题目说花圃是个环形,怎么办的?其实好办。我们令\(dp[M][s]=1\),然后一路转移到\(dp[N+M]\),这时取\(dp[N+M][s]\)作为\(s\)为初始状态(前\(M\)个花圃)的答案。因为前\(M\)个花圃等同于\(N+1..M\)的花圃。他们的状态吻合了(都是\(s\))就对了。所以我们需要所有可行的枚举\(s\)作为初始状态。答案累积

仅仅就是递推?那是否可以,矩阵乘法??

矩阵乘法优化递推,然而这道题还略微有些复杂。

首先我们可以改写一下方程,使得它更加具备\(Floyd\)的外貌。

不如浪费一层循环,去扫一个状态\(k\)。使得如果\(k\)可以转移到\(j\),那么$$dp[i][j]=\sum\limits_{}dp[i-1][k]$$或者进一步,我们连\(if\)语句也省略掉,预处理一个布尔数组,其中\(b[k][j]\)表示\(k\)能否转移到\(j\)。那么$$dp[i][j]=\sum\limits_{}dp[i-1][k]*b[k][j]$$这样一来,这个式子就是标准的矩阵乘法了。我们可以忽略\(i\)的存在,它的结果就等同于初始的\(dp\)数组乘以这个布尔数组的\(N\)次方

于是我们就可以利用矩阵乘法将\(b\)数组做一个矩阵快速幂。

答案究竟是什么?

答案究竟是什么?我们究竟用什么来乘这个\(N\)次方的结果?

我们还是参考刚才的状压做法。枚举一个\(s\),此时只有\(dp[M][s]\)为1,其他都为0. 那么我们可以想象我们其实有\(32\)个初始的项,分别是\(dp[M][0],dp[M][1],..,dp[M][s],..,dp[M][32]\)。其中只有\(dp[M][s]\)为1. 每乘一次矩阵就刷新一遍,成为\(dp[M+1][0],dp[M+1][1],..,dp[M+1][s],..,dp[M+1][32]\).直到刷新\(N\)遍以后变成\(dp[M+N][0],dp[M+N][1],..,dp[M+N][s],..,dp[M+N][32]\)。然而对于每一个\(s\),我们只需要取\(dp[N+M][s]\)作为答案。

考虑我们的\(dp[N+M][s]\)是怎么得来的?是\(dp\)矩阵依次乘以布尔矩阵的第\(s\)列的和。而我们刚才说了,只有\(dp[N+M][s]\)为1,其他都是0。因此结果就等同于是\(b[s][s]\)这一项。

我们发现根本不用枚举\(s\)!也就是说,只需要做一遍矩阵乘法——因为对于状态\(s\),对应的答案一定是\(b[s][s]\)。那么最终的答案也就是所有的\(b[s][s]\)?即矩阵的对角线之和!当然,并不是整一条对角线,要确保\(s\)是合法的。

反思

思维难度及其深的一道题,不强求满分做法,就看状压那部分吧。最关键就是想到有哪些因数能够影响我这一步的决策。动态规划应对多阶段决策问题时一般都需要这样考虑。仔细分析就会发现只有最近的\(M\)个才会有影响,然而题目又出乎意料的给了\(M \leq 5\),状压就很明显了

Code

注意矩阵存的是真正的状态,而不是状态的编号……

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define int ll
const int MAXN = 100010;
const int MOD = 1e9+7;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
int x = 0; int w = 1; register char c = getchar();
for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
if(c == '-') w = -1, c = getchar();
for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
int N,M,K,Ans;
int status[40],cnt,sta,_j,_k,exist[40];
struct Matrix{
int a[40][40];
inline void clear(){
memset(a,0,sizeof a);
}
inline void set_unit(){
memset(a,0,sizeof a);
for(int i = 0; i <= 32; ++i){
a[i][i] = 1;
}
}
};
inline Matrix mul(const Matrix a, const Matrix b){
Matrix tmp,res; tmp.clear(),res.clear();
for(int i = 0; i <= 32; ++i){
for(int j = 0; j <= 32; ++j){
for(int k = 0; k <= 32; ++k){
tmp.a[i][j] = (tmp.a[i][j] + a.a[i][k] * b.a[k][j]) % MOD;
}
}
}
for(int i = 0; i <= 32; ++i){
for(int j = 0; j <= 32; ++j){
res.a[i][j] = tmp.a[i][j];
}
}
return res;
}
Matrix ans,a;
inline bool check(int s){
int res(0);
while(s){
if(s & 1) ++res;
s >>= 1;
}
return res <= K;
}
void dfs(int x, int s){
if(x == M){
if(check(s)){
status[++cnt] = s;
a.a[(s>>1)+(1<<(M-1))][s] = 1;
a.a[s>>1][s] = 1;
exist[s] = 1;
}
return;
}
dfs(x+1, s);
dfs(x+1, s+(1<<x));
}
inline void ksm(int k){
while(k > 0){
if(k & 1) ans = mul(ans, a);
a = mul(a, a);
k /= 2;
}
}
signed main(){
N = read(), M = read(), K = read();
dfs(0, 0);
ans.set_unit();
ksm(N);
for(int i = 0; i <= 32; ++i){
if(exist[i]){
Ans = (Ans + ans.a[i][i]) % MOD;
}
}
printf("%lld", Ans);
return 0;
}

[洛谷P1357] 花园的更多相关文章

  1. 题解:洛谷P1357 花园

    题解:洛谷P1357 花园 Description 小 L 有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为 \(1∼n\).花园 \(1\) 和 \(n\) 是相邻的. 他的环形花园每天都会换 ...

  2. 洛谷 P1357 花园 解题报告

    P1357 花园 题目描述 小\(L\)有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为\(1~N(2<=N<=10^{15})\).他的环形花园每天都会换一个新花样,但他的花园都不 ...

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

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

  4. 洛谷 P1357 花园

    题意简述 一个只含字母C和P的环形串 求长度为n且每m个连续字符不含有超过k个C的方案数 题解思路 由于\(m<=5\)所以很显然状压 但由于\(n<=10^{15}\).可以考虑用矩阵加 ...

  5. 洛谷教主花园dp

    洛谷-教主的花园-动态规划   题目描述 教主有着一个环形的花园,他想在花园周围均匀地种上n棵树,但是教主花园的土壤很特别,每个位置适合种的树都不一样,一些树可能会因为不适合这个位置的土壤而损失观赏价 ...

  6. 洛谷 P2056 BZOJ 2743 [HEOI2012]采花

    //表示真的更喜欢洛谷的题面 题目描述 萧芸斓是 Z国的公主,平时的一大爱好是采花. 今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花.花园足够大,容纳了 n 朵花,花有 c 种颜色(用整数 ...

  7. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  8. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

  9. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

随机推荐

  1. Dynamics 365的审核日志分区删除超时报错怎么办?

    摘要: 本人微信公众号:微软动态CRM专家罗勇 ,回复296或者20190112可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me ...

  2. Dynamics 365支持的语言(中文语言名/英文语言名)列表

    本人微信和易信公众号:微软动态CRM专家罗勇 ,回复277或者20180803可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me ...

  3. Fragment与Activity的接口回调

    这里说一个官方推荐的写法: private OnFragmentInteractionListener mListener; @Override public void onAttach(Contex ...

  4. android 仿微信表情雨下落!

    文章链接:https://mp.weixin.qq.com/s/yQXn-YjEFSW1X7A7CcuaVg 众所周知,微信聊天中我们输入一些关键词会有表情雨下落,比如输入「生日快乐」「么么哒」会有相 ...

  5. Mysql增量写入Hdfs(二) --Storm+hdfs的流式处理

    一. 概述 上一篇我们介绍了如何将数据从mysql抛到kafka,这次我们就专注于利用storm将数据写入到hdfs的过程,由于storm写入hdfs的可定制东西有些多,我们先不从kafka读取,而先 ...

  6. [20190416]11g下那些latch是Exclusive的.txt

    [20190416]11g下那些latch是Exclusive的.txt --//昨天测试了11g下那些latch是共享的,链接:--//是否反过来剩下的都是Exclusive的.继续测试: 1.环境 ...

  7. Linux分页机制之概述--Linux内存管理(六)

    1 分页机制 在虚拟内存中,页表是个映射表的概念, 即从进程能理解的线性地址(linear address)映射到存储器上的物理地址(phisical address). 很显然,这个页表是需要常驻内 ...

  8. 爬虫之Requests&beautifulsoup

    网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕 ...

  9. 【Python 08】汇率兑换2.0-1(字符串索引)

     1.案例描述 设计一个汇率换算程序,其功能是将人民币转换为美元,或者美元转换为人民币. 增加功能:根据输入判断是人民币还是美元,进行相应的转换计算. 2.案例分析 3.字符串 两个双引号或单引号括起 ...

  10. 错误ERROR datanode.DataNode (DataXceiver.java:run(278)) - hadoop07:50010DataXceiver error processing unknown operation src:127.0.0.136479 dst:127.0.0.150010

    原因: Ambari 每分钟会向datanode发送"ping"连接一下去确保datanode是正常工作的.否则它会触发alert.但是datanode并没有处理空内容的逻辑,所以 ...