bzoj2004 矩阵快速幂优化状压dp
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的更多相关文章
- bzoj2004(矩阵快速幂,状压DP)
每个长度为p的区间都必须出现k次1,数据又很小,我们使用状压. dp[i][j]->dp[i+1][j'],dp[i][j]表示当前考虑到了第i个车站,包括第i个其后的p个的状态(有车停或没车停 ...
- BZOJ2004 HNOI2010公交线路(状压dp+矩阵快速幂)
由数据范围容易想到矩阵快速幂和状压. 显然若要满足一辆公交车的相邻站台差不超过p,则每相邻p个站台中每辆车至少经过一个站台.可以发现这既是必要的,也是充分的. 开始的时候所有车是相邻的.考虑每次把一辆 ...
- 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个 ...
- POJ 3744 【矩阵快速幂优化 概率DP】
搞懂了什么是矩阵快速幂优化.... 这道题的重点不是DP. /* 题意: 小明要走某条路,按照个人兴致,向前走一步的概率是p,向前跳两步的概率是1-p,但是地上有地雷,给了地雷的x坐标,(一维),求小 ...
- 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 ...
- BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*
BZOJ5298 CQOI2018 交错序列 [DP+矩阵快速幂优化] Description 我们称一个仅由0.1构成的序列为"交错序列",当且仅当序列中没有相邻的1(可以有相邻 ...
- 省选模拟赛 Problem 3. count (矩阵快速幂优化DP)
Discription DarrellDarrellDarrell 在思考一道计算题. 给你一个尺寸为 1×N1 × N1×N 的长条,你可以在上面切很多刀,要求竖直地切并且且完后每块的长度都是整数. ...
- HDU 5863 cjj's string game ( 16年多校10 G 题、矩阵快速幂优化线性递推DP )
题目链接 题意 : 有种不同的字符,每种字符有无限个,要求用这k种字符构造两个长度为n的字符串a和b,使得a串和b串的最长公共部分长度恰为m,问方案数 分析 : 直觉是DP 不过当时看到 n 很大.但 ...
- 2018.10.22 bzoj1009: [HNOI2008]GT考试(kmp+矩阵快速幂优化dp)
传送门 f[i][j]f[i][j]f[i][j]表示从状态"匹配了前i位"转移到"匹配了前j位"的方案数. 这个东西单次是可以通过跳kmp的fail数组得到的 ...
随机推荐
- 在idea中设置记住git的用户名和密码
在idea中设置记住git的用户名和密码 1.在项目根目录下执行以下git命令: git config --global credential.helper store 2.执行上述命令后,在idea ...
- BZOJ3291Alice与能源计划——匈牙利算法+模拟费用流
题目描述 在梦境中,Alice来到了火星.不知为何,转眼间Alice被任命为火星能源部长,并立刻面临着一个严峻的考验.为 了方便,我们可以将火星抽象成平面,并建立平面直角坐标系.火星上一共有N个居民点 ...
- #195 game(动态规划+二分)
考虑第一问的部分分.显然设f[i]为i子树从根开始扩展的所需步数,考虑根节点的扩展顺序,显然应该按儿子子树所需步数从大到小进行扩展,将其排序即可. 要做到n=3e5,考虑换根dp.计算某点答案时先将其 ...
- 查询SQLSERVER中系统所有表
SQL 查询所有表名: SELECT NAME FROM SYSOBJECTS WHERE TYPE='U' SELECT * FROM INFORMATION_SCHEMA.TABLES 查询表的所 ...
- python调用opencv库教程
OpenCV安装pip install --upgrade setuptoolspip install numpy Matplotlibpip install opencv-python OpenCV ...
- Codeforces1063D Candies for Children 【分类讨论】【暴力】
题目分析: 首先要想两个暴力,一个的时间复杂度是$O(n^2)$,另一个是$O([\frac{n}{k}])$的. $n^2$的暴力可以枚举两段,一段有$i$个取两个的小朋友,一段有$j$个取两个的小 ...
- windows环境pip安装时一直报错Could not fetch URL https://pypi.org/simple/xrld/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url:
最近项目不忙了~~有开始专研的python大业,上来想用pip安装一个第三方的库,就一直报错: Could not fetch URL https://pypi.org/simple/xrld/: T ...
- robotframework测试用例加入注释
*** Variables ***${HOST} 192.168.132.135${USER} username*** Test Cases ***Simple [Documentation] Sim ...
- 【Luogu4916】魔力环(Burnside引理,组合计数)
考虑\(Burside\)引理,设\(f(x)\)表示置换拆成循环的个数为\(x\)时的答案,那么最终的结果就是\(\displaystyle \frac{\sum_{i=1}^n f(gcd(i,n ...
- 【BZOJ5318】[JSOI2018]扫地机器人(动态规划)
[BZOJ5318][JSOI2018]扫地机器人(动态规划) 题面 BZOJ 洛谷 题解 神仙题.不会.... 先考虑如果一个点走向了其下方的点,那么其右侧的点因为要被访问到,所以必定只能从其右上方 ...