洛谷2151[SDOI2009]HH去散步(dp+矩阵乘法优化)
一道良好的矩阵乘法优化\(dp\)的题。
首先,一个比较\(naive\)的想法。
我们定义\(dp[i][j]\)表示已经走了\(i\)步,当前在点\(j\)的方案数。
由于题目中限制了不能立即走之前走过来的那个点,所以这个状态并不能优秀的转移。
尝试重新定义\(dp\)状态。
令\(dp[i][j]\)表示已经走了\(i\)步,当前在\(j\)这条边的终点的那个点。
假设\(to[j]=p\)
那么\(dp[i][j]\)可以转移到\(dp[i+1][out[p]] 其中\ (out[p]不为j的反向边)\)
其中\(out[p]\)表示p的出边(我们把题目中的每条无向拆成两个有向边)
最后求\(ans\)的时候,只需要枚举哪些边的终点是目标点,然后加起来即可
通过具体的边的限制,我们就能满足题目中的那个要求。
qwq但是我们发现,如果暴力转移的话,时间复杂度是不能够接受的。
考虑到每次只从\(i\)转移到\(i+1\)。
所以可以构造一个转移矩阵。
对于一个状态\(dp[x][i]\),然后在如果他能对编号为\(j\)的边产生贡献,那么我们把构造矩阵\(a[i][j]\)++
for (int i=1;i<=cnt;i++)
{
int to = y[i];
for (int j=0;j<out[to].size();j++)
{
int now = out[to][j];
if((i+1)==((now+1)^1)) continue;
b.a[i][now]++;
}
}
注意不能通过具体的点来判断,而要判断是否为反向边。
然后我们强行令初始矩阵为dp[1][i]的值,就是强行走一步,然后快速幂出来\(k-1\)次方的值,二者相乘,最后求解即可。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define mk make_pair
#define pb push_back
#define ll long long
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 150;
const int maxm = 1e5+1e2;
const int mod = 45989;
struct Ju{
int x,y;
int a[maxn][maxn];
Ju operator * (Ju b)
{
Ju ans;
memset(ans.a,0,sizeof(ans.a));
ans.x=x;
ans.y=b.y;
for (register int i=1;i<=ans.x;++i)
for (register int j=1;j<=ans.y;++j)
for (register int k=1;k<=y;++k)
ans.a[i][j]=(ans.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
return ans;
}
};
Ju qsm(Ju i,int j)
{
Ju ans;
memset(ans.a,0,sizeof(ans.a));
ans.x=i.x;
ans.y=i.y;
for (int p=1;p<=i.x;p++) ans.a[p][p]=1;
while(j)
{
if (j&1) ans=ans*i;
i=i*i;
j>>=1;
}
return ans;
};
Ju a,b;
int n,m,k,s,t;
int x[maxm],y[maxm],w[maxm];
int cnt=0;
vector<int> in[maxn],out[maxn];
signed main()
{
n=read();m=read(),k=read(),s=read(),t=read();
s++;
t++;
for (int i=1;i<=m;i++)
{
int u=read(),v=read();
u++;
v++;
++cnt;
x[cnt]=u,y[cnt]=v;
++cnt;
x[cnt]=v,y[cnt]=u;
}
for (int i=1;i<=cnt;i++)
{
out[x[i]].pb(i);
in[y[i]].pb(i);
}
for (int i=1;i<=cnt;i++)
{
int to = y[i];
for (int j=0;j<out[to].size();j++)
{
int now = out[to][j];
if((i+1)==((now+1)^1)) continue;
b.a[i][now]++;
}
}
//for (int i=1;i<=cnt;i++)
// {
// for (int j=1;j<=cnt;j++) cout<<b.a[i][j]<<" ";
// cout<<endl;
//}
for (int i=0;i<out[s].size();i++)
{
a.a[1][out[s][i]]++;
//cout<<out[s][i]<<" "<<endl;
}
//cout<<"******************"<<endl;
//for (int i=1;i<=cnt;i++) cout<<a.a[1][i]<<" ";
//cout<<endl;
a.x=1;
a.y=cnt;
b.x=cnt;
b.y=cnt;
b=qsm(b,k-1);
a=a*b;
int ans = 0;
for (int i=1;i<=cnt;i++)
{
if (y[i]==t) ans=(ans+a.a[1][i])%mod;
//cout<<ans<<endl;
}
cout<<ans;
return 0;
}
洛谷2151[SDOI2009]HH去散步(dp+矩阵乘法优化)的更多相关文章
- BZOJ.1875.[SDOI2009]HH去散步(DP 矩阵乘法)
题目链接 比较容易想到用f[i][j]表示走了i步后到达j点的方案数,但是题目要求不能走上一条走过的边 如果这样表示是不好转移的 可以考虑边,f[i][j]表示走了i步后到达第j条边的方案数,那么有 ...
- BZOJ_1875_[SDOI2009]HH去散步_矩阵乘法
BZOJ_1875_[SDOI2009]HH去散步_矩阵乘法 Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时H ...
- BZOJ 1875: [SDOI2009]HH去散步( dp + 矩阵快速幂 )
把双向边拆成2条单向边, 用边来转移...然后矩阵乘法+快速幂优化 ------------------------------------------------------------------ ...
- BZOJ-1875 HH去散步 DP+矩阵乘法快速幂
1875: [SDOI2009]HH去散步 Time Limit: 20 Sec Memory Limit: 64 MB Submit: 1196 Solved: 553 [Submit][Statu ...
- 洛谷P2151 [SDOI2009] HH去散步 [矩阵加速]
题目传送门 HH去散步 题目描述 HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走 ...
- [bzoj1875] [洛谷P2151] [SDOI2009] HH去散步
Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又 ...
- 1875. [SDOI2009]HH去散步【矩阵乘法】
Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又 ...
- 洛谷 P2151 [SDOI2009]HH去散步
题目链接 思路 如果没有不能走上一条边的限制,很显然就是dp. 设f[i][j]表示到达i点走了j步的方案数,移到k点可以表示为f[k][j+1]+=f[i][j]. 如果有限制的话,可以考虑用边表示 ...
- [bzoj1875][SDOI2009] HH去散步 [dp+矩阵快速幂]
题面 传送门 正文 其实就是让你求有多少条长度为t的路径,但是有一个特殊条件:不能走过一条边以后又立刻反着走一次(如果两次经过同意条边中间隔了别的边是可以的) 如果没有这个特殊条件,我们很容易想到dp ...
随机推荐
- redis集群访问,重启,关闭,带密码访问集群
安装ruby后查找如下文件 vi 进去后编辑 此处编写自己的密码,重启后便可带密码访问集群 随便选择一个节点输入如下指令查看集群信息 正常关闭redis命令如下: 重启redis集群再次以相同的命 ...
- Seq2Seq sequence-to-sequence模型 简介
Sequence-to-sequence (seq2seq) 模型. 突破了传统的固定大小输入问题框架 开创了将DNN运用于翻译.聊天(问答)这类序列型任务的先河 并且在各主流语言之间的相互翻译,和语 ...
- MySQL读写IO的操作过程解析
数据库作为存储系统,所有业务访问数据的操作都会转化为底层数据库系统的IO行为(缓存系统也可以当做是key-value的数据库),本文主要介绍访问MySQL数据库的IO流程以及IO相关的参数. 一.My ...
- CPU内部结构域寄存器
CPU内部结构域寄存器 64位和32位系统区别: 寄存器是CPU内部最基本的存储单元. CPU对外是通过总线(地址.控制.数据)来和外部设备交互的,总线的宽度是8位,同时CPU的寄存器也是8位,那 ...
- 基于Linux系统的网络服务——高速缓存DNS及企业级域名解析服务
1.DNS域名系统 DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数 ...
- 第17章-x86-64寄存器
不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Architecture),也可以称为指令集(instruction set).Intel将x86系列CP ...
- ubantu虚拟机搭建xl2tp服务
在编译成功源码,安装完毕xl2tpd后,便可以配置xl2tpd服务.基本配置过程主要涉及两个配置文件:一个用来配置xl2tpd, 一个用来配置ppp协议.下面分别对这两个文件进行说明,最后添加xl2t ...
- JS009. 数组去重的多种方法总结与一步步优化
两层for循环 这种函数的优点是兼容性好比较通用,缺点是时空复杂度都很直观的为O(n2),不利于维护和性能. var array = [1,1,'1','1'] function unique(arr ...
- centos7关于防火墙的一些操作
防火墙相关 # 检查防火墙状态 systemctl status firewalld # 开启防火墙 systemctl start firewalld # 关闭防火墙 systemctl stop ...
- Sentry 后端监控 - 最佳实践(官方教程)
系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...