Dangerous Sugoroku:赛时写了矩乘 T 飞了,受到 sunkuangzheng 大佬的启发才知道要状压矩乘。

暴力矩乘思路

直接像过河那样写模拟细节非常多,于是考虑像美食家一样的思路,利用矩阵分段加速。

定义 \(dp_i\) 表示 \(i\) 能否到达,则有如下转移:

\[dp_{i}=\bigvee_{j=i-B}^{i-A}dp_{j}
\]

因为 \(A,B\le 20\),所以可用状态非常少,就可以用矩阵优化了。

那么很显然就把段拆一下,然后直接跑矩乘就好了。这个做法甚至连 \(m=0,l_i-1=r_{i-1}\) 的细节都不用判。

代码如下,如果你真这么写了,会收获 TLE 和 WA 的好成绩!

下面这份暴力中 \(dp_i\) 表示 \(dp_i\) 的方案数,所以转移有所不同,本质还是和上面一样的。只要 \(dp_i>0\) 就能到达。

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
struct mat{
ull a[25][25];
mat(){memset(a,0,sizeof(a));}
mat operator*(const mat &x)const{
mat res;
for(int i=0;i<=20;i++)
{
for(int k=0;k<=20;k++)
{
ull l=a[i][k];
for(int j=0;j<=20;j++)
{
res.a[i][j]=(res.a[i][j]+l*x.a[k][j]);
}
}
}
return res;
}
}s,dp1,dp2;
mat qpow(mat x,ll k)
{
mat res;
for(int i=0;i<=20;i++)res.a[i][i]=1;
while(k>0)
{
if(k&1)res=res*x;
x=x*x;
k>>=1;
}
return res;
}
ll n,m,a,b,l[200005],r[200005];
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>a>>b;
s.a[1][20]=1;
for(int i=1;i<=19;i++)dp1.a[i+1][i]=dp2.a[i+1][i]=1;
for(int i=20-a+1;i>=20-b+1;i--)dp1.a[i][20]=1;
r[0]=1;
for(int i=1;i<=m;i++)
{
cin>>l[i]>>r[i];
if(r[i]-l[i]+1>=21||l[i]==1)
{
cout<<"No";
return 0;
}
if(l[i]-r[i-1]-1>0)s=s*qpow(dp1,l[i]-r[i-1]-1);
if(r[i]-l[i]+1>0)s=s*qpow(dp2,r[i]-l[i]+1);
}
if(n+1-r[m]-1>0)s=s*qpow(dp1,n+1-r[m]-1);
if(s.a[1][20])cout<<"Yes";
else cout<<"No";
return 0;
}

原因是复杂度为 \(O(m\times 20^3 \log n)\),常数又大,无法通过。

优化矩乘

因为状态中只有 \(0\) 和 \(1\),每行每列的数又只有 \(20\) 个,考虑用状压与位运算去掉一个 \(20\)。

我们先看暴力的矩乘代码,再来考虑如何优化它。

struct mat{
ull a[25][25];
mat(){memset(a,0,sizeof(a));}
mat operator*(const mat &x)const{
mat res;
for(int i=0;i<=20;i++)
{
for(int k=0;k<=20;k++)
{
for(int j=0;j<=20;j++)
{
res.a[i][j]=(res.a[i][j]|(a[i][k]&x.a[k][j]));
}
}
}
return res;
}
}

可以发现,当 \(a_{i,k}=1\) 时,\(res_{i,j}\) 取决于 \(x_{i,j}\) 是否为 \(1\)。

因此,就可以省去循环 \(j\),当 \(a_{i,k}=1\) 时,直接让 \(res_i \gets res_i \vee x_k\) 即可。

时间复杂度 \(O(m\times 20^2 \log n)\),可以通过。

代码如下:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
struct mat{
ull a[25];
mat(){memset(a,0,sizeof(a));}
mat operator*(const mat &x)const{
mat res;
for(int i=0;i<=20;i++)
{
for(int k=0;k<=20;k++)
{
if((a[i]>>k)&1)res.a[i]=(res.a[i]|x.a[k]);
}
}
return res;
}
}s,dp1,dp2;
mat qpow(mat x,ll k)
{
mat res;
for(int i=0;i<=20;i++)res.a[i]=(1<<i);
while(k>0)
{
if(k&1)res=res*x;
x=x*x;
k>>=1;
}
return res;
}
ll n,m,a,b,l[200005],r[200005];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>a>>b;
s.a[1]=(1<<20);
for(int i=1;i<=19;i++)dp1.a[i+1]=dp2.a[i+1]=(1<<i);
for(int i=20-a+1;i>=20-b+1;i--)dp1.a[i]|=(1<<20);
r[0]=1;
for(int i=1;i<=m;i++)
{
cin>>l[i]>>r[i];
if(r[i]-l[i]+1>=21||l[i]==1)
{
cout<<"No";
return 0;
}
if(l[i]-r[i-1]-1>0)s=s*qpow(dp1,l[i]-r[i-1]-1);
if(r[i]-l[i]+1>0)s=s*qpow(dp2,r[i]-l[i]+1);
}
if(n+1-r[m]-1>0)s=s*qpow(dp1,n+1-r[m]-1);
if((s.a[1]>>20)&1)cout<<"Yes";
else cout<<"No";
return 0;
}

Atcoder ABC388F Dangerous Sugoroku 题解 [ 蓝 ] [ 矩阵加速 ] [ 状压矩乘 ] [ 模拟 ]的更多相关文章

  1. 【题解】P3959 宝藏 - 状压dp / dfs剪枝

    P3959 宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的m  条道路和它们的长度. 小明决心亲自前往挖掘所有宝 ...

  2. Bzoj 3813 奇数国 题解 数论+线段树+状压

    3813: 奇数国 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 748  Solved: 425[Submit][Status][Discuss] ...

  3. NOIp2016 D2T3 愤怒的小鸟【搜索】(网上题解正解是状压)

    题目传送门 没啥别的想法,感觉就是搜索,经过原点的抛物线已知两个点就可以求出解析式,在还没有被打下来的两个猪之间随意配对,确定解析式之后标记在这个抛物线下被打下来的猪. 猪也可以单独用一个抛物线打下来 ...

  4. 【题解】P2831 愤怒的小鸟 - 状压dp

    P2831愤怒的小鸟 题目描述 \(Kiana\) 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 \((0,0)\) 处,每次 \(Kiana\) 可以 ...

  5. 洛谷 P1939 矩阵加速(数列)

    题意简述 \(a[1]=a[2]=a[3]=1\) \(a[x]=a[x−3]+a[x−1](x>3)\) 求a数列的第n项对1000000007取余的值. 题解思路 矩阵加速 设\[ F=\b ...

  6. HDU 5434 Peace small elephant 状压dp+矩阵快速幂

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5434 Peace small elephant  Accepts: 38  Submissions: ...

  7. BZOJ2004:[HNOI2010]Bus 公交线路(状压DP,矩阵乘法)

    Description 小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km. 作为公交车线路的规划者,小Z调查了市民的需求,决定 ...

  8. 【KMP】【矩阵加速】【递推】洛谷 P3193 [HNOI2008]GT考试 题解

        看出来矩阵加速也没看出来KMP…… 题目描述 阿申准备报名参加 GT 考试,准考证号为\(N\)位数\(X_1,X_2…X_n(0\le X_i\le9)\),他不希望准考证号上出现不吉利的数 ...

  9. 洛谷P3502 [POI2010]CHO-Hamsters感想及题解(图论+字符串+矩阵加速$dp\&Floyd$)

    洛谷P3502 [POI2010]CHO-Hamsters感想及题解(图论+字符串+矩阵加速\(dp\&Floyd\)) 标签:题解 阅读体验:https://zybuluo.com/Junl ...

  10. [模板][题解][Luogu1939]矩阵乘法加速递推(详解)

    题目传送门 题目大意:计算数列a的第n项,其中: \[a[1] = a[2] = a[3] = 1\] \[a[i] = a[i-3] + a[i - 1]\] \[(n ≤ 2 \times 10^ ...

随机推荐

  1. Clickhouse之集群操作

    查看集群: 在任意一台机上,使用 /usr/bin/clickhouse-client --host localhost --port 9000 连接本地服务器 select * from `syst ...

  2. nginx相关服务实践

    1. 实现客户端IP地址获取接口 普通版本 Nginx 的配置文件中提供了一个变量 $remote_addr 用来获取用户访问本实例时的 IP 地址,我们只要将这个变量的值返回给用户就行了(没错,就是 ...

  3. 使用LOGSTASH 将数据导入到ES

    logstash 执行过程 input -->filter -->output filter 可以对数据进行处理 输出插件 codec plugin 使用脚本将数据导入到ES input ...

  4. HWS 2023 山东大学专场 决赛wp

    写在前面: 通过这次比赛,认识到了自己和高手的不足,知识面还需要拓展 1.Re ezapk 因为自己本身不是做逆向的,所以没什么经验,赛后复盘的时候解决了这道题 首先使用apktool将给出的apk文 ...

  5. Xcode Swift自动格式化

    Xcode Swift自动格式化 安装SwiftFormat SwiftFormat 是一款用来格式化Swift代码的命令行工具. 直接用Homebrew安装 brew install swiftfo ...

  6. javax.management.InstanceNotFoundException: org.springframework.boot:type=Admin,name=SpringApplicati

    把这两个勾选去掉

  7. Qt编写可视化大屏电子看板系统28-模块6送检合格

    一.前言 送检合格率模块包括钢件合格率.电机合格率.当天合格率.模具零件合格率四个子模块,其中钢件合格率和电极合格率都是采用的曲线图展示,统计的15天内的合格率,定位线放在90这个值的位置,相当于90 ...

  8. Qt音视频开发44-实时人脸框

    一.前言 在人脸识别到以后,需要在实时视频上将所有人脸框绘制出来,一把来说识别人脸会有多种选择,一个是识别最大人脸,这种场景主要用于刷脸门禁,还有一种是识别所有人脸,这种场景主要用于人脸识别摄像机,就 ...

  9. Qt编写项目作品26-一维码二维码解析及生成

    一.功能特点 支持本地USB摄像头实时解析. 支持网络视频流实时解析. 解码格式支持一维码二维码等各种编码. 可生成一维码二维码,一维码支持EAN_13格式,其他格式可定制. 条形码参数支持宽度.高度 ...

  10. [转]CMake与Make最简单直接的区别

    写程序大体步骤为: 1.用编辑器编写源代码,如.c文件. 2.用编译器编译代码生成目标文件,如.o. 3.用链接器连接目标代码生成可执行文件,如.exe. 但如果源文件太多,一个一个编译时就会特别麻烦 ...