一道良好的矩阵乘法优化\(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+矩阵乘法优化)的更多相关文章

  1. BZOJ.1875.[SDOI2009]HH去散步(DP 矩阵乘法)

    题目链接 比较容易想到用f[i][j]表示走了i步后到达j点的方案数,但是题目要求不能走上一条走过的边 如果这样表示是不好转移的 可以考虑边,f[i][j]表示走了i步后到达第j条边的方案数,那么有 ...

  2. BZOJ_1875_[SDOI2009]HH去散步_矩阵乘法

    BZOJ_1875_[SDOI2009]HH去散步_矩阵乘法 Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时H ...

  3. BZOJ 1875: [SDOI2009]HH去散步( dp + 矩阵快速幂 )

    把双向边拆成2条单向边, 用边来转移...然后矩阵乘法+快速幂优化 ------------------------------------------------------------------ ...

  4. BZOJ-1875 HH去散步 DP+矩阵乘法快速幂

    1875: [SDOI2009]HH去散步 Time Limit: 20 Sec Memory Limit: 64 MB Submit: 1196 Solved: 553 [Submit][Statu ...

  5. 洛谷P2151 [SDOI2009] HH去散步 [矩阵加速]

    题目传送门 HH去散步 题目描述 HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走 ...

  6. [bzoj1875] [洛谷P2151] [SDOI2009] HH去散步

    Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又 ...

  7. 1875. [SDOI2009]HH去散步【矩阵乘法】

    Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又 ...

  8. 洛谷 P2151 [SDOI2009]HH去散步

    题目链接 思路 如果没有不能走上一条边的限制,很显然就是dp. 设f[i][j]表示到达i点走了j步的方案数,移到k点可以表示为f[k][j+1]+=f[i][j]. 如果有限制的话,可以考虑用边表示 ...

  9. [bzoj1875][SDOI2009] HH去散步 [dp+矩阵快速幂]

    题面 传送门 正文 其实就是让你求有多少条长度为t的路径,但是有一个特殊条件:不能走过一条边以后又立刻反着走一次(如果两次经过同意条边中间隔了别的边是可以的) 如果没有这个特殊条件,我们很容易想到dp ...

随机推荐

  1. HCNP Routing&Switching之OSPF LSA类型

    前文我们了解了OSPF中的虚连接相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15202348.html:今天我们来聊一聊OSPF数据包中LSA类型相 ...

  2. mysql switch语句

    SELECT CASE the_order_status WHEN 4 THEN '待收货' WHEN 5 THEN '已收货' ELSE '其他' END AS statuss ,order_id ...

  3. 786. 第k个数

    题目传送门 一.理解感悟 1.这是快速排序模板的练习题. 2.不一样的地方在于它可以利用快排模板,但却不需要真的把所有数据排序完成,每次一分为二后,只关心自己所有的那一半,就是可以节约一半的递归. 3 ...

  4. 浅析 Dapr 里的云计算设计模式

    Dapr 实际上是把分布式系统 与微服务架构实践的挑战以及k8s 这三个主题的全方位的设计组合,特别是Kubernetes设计模式 一书作者Bilgin Ibryam 提出的Multi-Runtime ...

  5. 解决方案-问题001:物理机、虚机等等Linux操作系统/usr/bin目录权限误操作,导致无法切换root

    导语:平常运维人员会误操作一些目录权限,导致一些问题,那么如何恢复呢? 问题:物理机.虚机等等Linux操作系统/usr/bin目录权限误操作,导致无法切换root? 实验环境: ip地址 是否目录正 ...

  6. Onenote实现OCR识别图片

    OCR识别推荐两个软件: 1.       Tesseract:一个开源的,由谷歌维护的OCR软件. 2.       Onenote:微软Office附带或者可以自己独立安装. 3.       O ...

  7. 【曹工杂谈】Maven底层容器Plexus Container的前世今生,一代芳华终落幕

    Maven底层容器Plexus Container的前世今生,一代芳华终落幕 前言 说实话,我非常地纠结,大家平时只是用Maven,对于内部的实现其实也不关心,我现在非要拉着大家给大家讲.这就有个问题 ...

  8. AI学习1

    什么是AI: 功能介绍:是一种应用于出版.多媒体和在线图像的工业标准矢量插画的软件,是一款非常好的矢量图形处理工具应用:标志设计.字体设计.印刷出版.海报书籍排版.专业插画.多媒体图像处理和互联网页面 ...

  9. Vue项目中应用TypeScript

    一.前言 与如何在React项目中应用TypeScript类似 在VUE项目中应用typescript,我们需要引入一个库vue-property-decorator, 其是基于vue-class-c ...

  10. 【OI】C++STL初步 排序与检索

    从紫皮书过来的,但是书中内容讲的比较简洁,做一点补充笔记. 一.排序(sort函数) 头文件:<algorithm> 语法:sort(start,end,cmp); start,end必须 ...