一道良好的矩阵乘法优化\(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. Vue.JS快速上手(组件间的通信)

    前言 Vue采用的是组件化思想,那么这些组件间是如何通信的呢?下面详细介绍一下. 所谓组件间通信,不单单是我们字面上理解的相互传递数据,这里还包括一个组件访问另一个组件的实例方法等,如父组件通过ref ...

  2. 5M1E,软件质量管理最佳解决方案

    - 如何做好一个产品? - 用户.需求.文化.价值.设计.流程,这些因素缺一不可.- 那么,如何做好产品的质量管理?- 人.机器.物料.方法.环境.测量,这些因素同样缺一不可.能够影响产品质量波动的因 ...

  3. BUUCTF-[CISCN2019 华东北赛区]Web2

    BUUCTF-[CISCN2019 华东北赛区]Web2 看题 一个论坛,内容不错:) 可以投稿,点击投稿发现要注册,那就先注册登录.随便账号密码就行. 常规操作,扫一下站点,发现有admin.php ...

  4. Eclipse中安装配置Gradle

    Gradle是以Groovy语言为基础,面向Java应用为主.基于DSL(领域特定语言)语法的自动化构建工具. gradle对多工程的构建支持很出色,工程依赖是gradle的第一功能. gradle支 ...

  5. GIMP 一键均匀添加多条参考线 一键均匀切分图片

    添加参考线 #!/usr/bin/env python2 # -*- coding: utf-8 -*- from gimpfu import * # orientation: ORIENTATION ...

  6. Java并发之锁升级:无锁->偏向锁->轻量级锁->重量级锁

    Java并发之锁升级:无锁->偏向锁->轻量级锁->重量级锁 对象头markword 在lock_bits为01的大前提下,只有当是否偏向锁位值为1的时候,才表明当前对象处于偏向锁定 ...

  7. Mybatis-Plus - 条件构造器 QueryWrapper 的使用

    目录 前言 查询示例 基础代码 QueryWrapper 的基本使用 QueryWrapper 的lambada写法 LambadaQueryWrapper 的使用 LambdaQueryChainW ...

  8. Smooth

      考场\(AC\),还是很开心的.   考虑这题让你干啥,就是给你一堆素数,然后让你用他们去构造数,求其中第\(k\)小的.   我们可以用系数累乘的方式,同时利用小根堆实现有序,加一个优化,就过了 ...

  9. docker-compose up -d nginx 报错

    在阿里云ECS上创建nginx容器时,报错如上图. The solution: In your Dockerfile, before running any apt commands, add the ...

  10. dede调用数据时,字符串替换函数使用

    {dede:sql sql="SELECT typename,typedir,typeimg FROM #@__arctype where topid=30 limit 0,6"} ...