https://www.lydsy.com/JudgeOnline/problem.php?id=2004

以前只会状压dp和矩阵快速幂dp,没想到一道题还能组合起来一起用,算法竞赛真是奥妙重重

小Z所在的城市有N个公交车站,排列在一条长(N-)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距
离均为1km。 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路:
.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站。
.每个车站必须被一辆且仅一辆公交车经过(始发站和
终点站也算被经过)。
.公交车只能从编号较小的站台驶往编号较大的站台。
.一辆公交车经过的相邻两个
站台间距离不得超过Pkm。 在最终设计线路之前,小Z想知道有多少种满足要求的方案。由于答案可能很大,你只
需求出答案对30031取模的结果。

题意

其实这不是一个难想的题目,P小于10的范围很容易就会想到去状态压缩,dp题的用意表达的也比较刻意,N的范围1e9又在含沙射影的告诉我这得矩乘优化。

这就得出了这题的大致算法,但是比较困难的事实上是怎么去方程转移怎么去优化。

和以前的套路一样,考虑先写一个朴素算法。看了题目很容易发现,在p公里的区间内,一定会出现K辆车停过的站牌,用dp[i][j]表示到了i这个位置,状态为j的数量个数。1表示这个位置有一辆车,0表示这个位置的车已经开到后面去了。

状态转移方程是每个状态考虑一个有车的位置开到i + 1这个位置的状态。由此我们可以推出一个朴素算法

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = ;
int N,M,K,P;
int dp[][ << ]; //在这个点之前p个位置的状态
int pre[ << ];
bool limit[ << ];
int usable[ << ],cnt;
void init(){
For(i,,( << P) - ){
int num = ;
For(j,,P - ){
if(i & ( << j)){
if(j == P - ) limit[i] = ;
num++;
}
}
if(num == K && (i & )) usable[++cnt] = i;
}
}
int main()
{
Sca3(N,K,P);
init();
int s = ;
For(i,,K - ) s |= ( << i);
dp[K & ][s] = ;
For(i,K + ,N){
Mem(dp[i & ],);
For(j,,cnt){
int t = usable[j];
if(limit[usable[j]]){
t ^= ( << (P - ));
t <<= ; t++;
dp[i & ][t] = (dp[i & ][t] + dp[i + & ][usable[j]]) % mod;
}else{
t <<= ; t++;
For(k,,P){
if(t & ( << k)) dp[i & ][t ^ ( << k)] = (dp[i & ][t ^ ( << k)] + dp[i + & ][usable[j]]) % mod;
}
}
}
}
Pri(dp[N & ][s]);
#ifdef VSCode
system("pause");
#endif
return ;
}

憨厚老实朴素算法

这是一个时间复杂度和空间复杂度双双爆炸的算法,空间我们可以用滚动数组来优化掉,但是1e9的N是无论如何也不可能去线性递推出来的。

当写完这样一个朴素算法的时候,快速矩阵幂就很容易写出来去优化了,

矩阵内数字的意义是这个状态下的数量个数,每次矩乘相当于到了下一个站牌状态数量的更新。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = ;
int N,M,K,P;
bool limit[ << ];
int id[ << ];
int usable[],cnt;
struct Mat{
LL a[][];
void init(){
Mem(a,);
}
};
Mat operator *(Mat a,Mat b){
Mat ans; ans.init();
For(i,,cnt){
For(j,,cnt){
For(k,,cnt){
ans.a[i][j] = (ans.a[i][j] + a.a[i][k] * b.a[k][j]) % mod;
}
}
}
return ans;
}
void init(){
cnt = -;
For(i,,( << P) - ){
int num = ;
For(j,,P - ) num += ((i & ( << j)) != );
limit[i] = (i & ( << (P - )));
if(num == K && (i & )){
usable[++cnt] = i;
id[i] = cnt;
}
}
}
void solve(){
int s = ( << K) - ;
Mat base,ans;
ans.init(); base.init();
ans.a[][id[s]] = ;
For(i,,cnt){
int t = usable[i];
if(limit[t]){
t ^= ( << (P - )); t <<= ; t++;
base.a[i][id[t]] = ;
}else{
t <<= ; t++;
For(k,,P){
if(t & ( << k)){
base.a[i][id[t ^ ( << k)]] = ;
}
}
}
}
N -= K;
while(N){
if(N & ) ans = ans * base;
base = base * base;
N >>= ;
}
Prl(ans.a[][id[s]]);
}
int main()
{
Sca3(N,K,P);
init();
solve();
#ifdef VSCode
system("pause");
#endif
return ;
}

bzoj2004 矩阵快速幂优化状压dp的更多相关文章

  1. bzoj2004(矩阵快速幂,状压DP)

    每个长度为p的区间都必须出现k次1,数据又很小,我们使用状压. dp[i][j]->dp[i+1][j'],dp[i][j]表示当前考虑到了第i个车站,包括第i个其后的p个的状态(有车停或没车停 ...

  2. BZOJ2004 HNOI2010公交线路(状压dp+矩阵快速幂)

    由数据范围容易想到矩阵快速幂和状压. 显然若要满足一辆公交车的相邻站台差不超过p,则每相邻p个站台中每辆车至少经过一个站台.可以发现这既是必要的,也是充分的. 开始的时候所有车是相邻的.考虑每次把一辆 ...

  3. 2018.10.16 uoj#340. 【清华集训2017】小 Y 和恐怖的奴隶主(矩阵快速幂优化dp)

    传送门 一道不错的矩阵快速幂优化dpdpdp. 设f[i][j][k][l]f[i][j][k][l]f[i][j][k][l]表示前iii轮第iii轮还有jjj个一滴血的,kkk个两滴血的,lll个 ...

  4. POJ 3744 【矩阵快速幂优化 概率DP】

    搞懂了什么是矩阵快速幂优化.... 这道题的重点不是DP. /* 题意: 小明要走某条路,按照个人兴致,向前走一步的概率是p,向前跳两步的概率是1-p,但是地上有地雷,给了地雷的x坐标,(一维),求小 ...

  5. 2018.10.23 bzoj1297: [SCOI2009]迷路(矩阵快速幂优化dp)

    传送门 矩阵快速幂优化dp简单题. 考虑状态转移方程: f[time][u]=∑f[time−1][v]f[time][u]=\sum f[time-1][v]f[time][u]=∑f[time−1 ...

  6. BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*

    BZOJ5298 CQOI2018 交错序列 [DP+矩阵快速幂优化] Description 我们称一个仅由0.1构成的序列为"交错序列",当且仅当序列中没有相邻的1(可以有相邻 ...

  7. 省选模拟赛 Problem 3. count (矩阵快速幂优化DP)

    Discription DarrellDarrellDarrell 在思考一道计算题. 给你一个尺寸为 1×N1 × N1×N 的长条,你可以在上面切很多刀,要求竖直地切并且且完后每块的长度都是整数. ...

  8. HDU 5863 cjj's string game ( 16年多校10 G 题、矩阵快速幂优化线性递推DP )

    题目链接 题意 : 有种不同的字符,每种字符有无限个,要求用这k种字符构造两个长度为n的字符串a和b,使得a串和b串的最长公共部分长度恰为m,问方案数 分析 : 直觉是DP 不过当时看到 n 很大.但 ...

  9. 2018.10.22 bzoj1009: [HNOI2008]GT考试(kmp+矩阵快速幂优化dp)

    传送门 f[i][j]f[i][j]f[i][j]表示从状态"匹配了前i位"转移到"匹配了前j位"的方案数. 这个东西单次是可以通过跳kmp的fail数组得到的 ...

随机推荐

  1. cuda编程-矩阵乘法(2)

    采用shared memory加速 代码 #include <stdio.h> #include <stdlib.h> #include <math.h> #inc ...

  2. wpgwhpg

    //f[i][j]就是第is时wpgwhpg的疲劳度是j,那么我们就可以就ta这1s是否休息进行讨论 #include<bits/stdc++.h> using namespace std ...

  3. ContOS7切换国内源

    ContOS更换国内下载源 一,什么是yum源? yum,是Yellow dog Updater, Modified 的简称,是杜克大学为了提高RPM 软件包安装性而开发的一种软件包管理器.起初是由y ...

  4. [洛谷P1730] 最小密度路径

    类型:Floyd 传送门:>Here< 题意:定义一条路径密度 = 该路径长度 / 边数.给出一张$DAG$,现有$Q$次询问,每次给出$X,Y$,问$X,Y$的最小密度路径($N \le ...

  5. Nginx 网站域名80 反向代理并且重定向到 tomcat 8080 网站固定页

    配置 server { listen 80 default_server; listen [::]:80 default_server; server_name :127.0.0.1:8080; 反向 ...

  6. Redhat 用代理连外网

    设置 /etc/yum.conf 添加proxy=http://web-proxy.corp.xx.com:8080 /etc/yum.repos.d/rhel-source.repo 里面改成ena ...

  7. rt-thread 学习路线

    @2019-01-25 [小记] > BSP制作与使用 将板载资源.芯片外设全部制作BSP,做到使用时只需在 menuconfig 中配置即用

  8. 关于Ubuntu18.04谷歌浏览器经常卡死的解决

    老电脑本来用的是Win系列,后来改成Linux后就不卡了,这几天同Notebook运行的Script开始复杂了,Ubuntu经常卡死(发公众号也经常卡死),本来以为是Ubuntu的问题 后来一想,不对 ...

  9. 区块链使用Java,以太坊 Ethereum, web3j, Spring Boot

    Blockchain is one of the buzzwords in IT world during some last months. This term is related to cryp ...

  10. CF528D Fuzzy Search

    题意:给定k,只含有ACGT的字符串S和T,求T在S中出现了多少次. 字符匹配:如果S的[i - k, i + k]中有字符x,那么第i位可以匹配x. 解: 首先预处理:f[i][j]表示S的第i位能 ...