题目H有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。 现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径.

对于100%的数据,$N ≤ 20,M ≤ 60,t ≤ 2^{30},0 ≤ A,B $

题解

既然n<=20,考虑开个邻接矩阵$table$存图

先考虑暴力,设$dp[i][j]$表示还需走i段路,当前走到了j这个点

易得$dp[i][j]=sum(dp[i+1][k]*table[j][k])+1$

然后无脑dfs即可

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int table[21][21],dp[21][11],n,a,b;
void dfs(int id,int from,int t)
{
//if(dp[id][from]) return;
if(!t)
{
dp[id][t]+=(id==b);
return;
}
for(int i=0;i<n;i++)
{
if(!table[id][i]||from==i) continue;
dfs(i,id,t-1);
dp[id][t]+=table[id][i]*dp[i][t-1];
dp[id][t]%=45989;
}
}
int main()
{
int m,t;
cin>>n>>m>>t>>a>>b;
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
table[a][b]=++table[b][a];
}
dfs(a,0,t);
cout<<dp[b][0]; }

发现i这一维可以滚动掉,然后我们发现这个方程的转移是固定不变
即,对于同一个j,都是同一批k来更新它。

那么,我们可以先处理一个另一个邻接矩阵$table2[k][i][j]$表示i在走了k步后到达j的方案书.

回忆floyd的过程,我们可以用类似的方法不断用$table2[k-1][i][j]$计算出新的$table2[k][i][j]$

然后发现其实这就是矩阵乘法,于是这个过程可以用矩阵快速幂来加速

这其实是一个很常见的图论dp trick,当然,这个是要在点数小到可以用邻接矩阵时才能用的

void mulit(int arr1[][130],int arr2[][130],int size)
{
memset(temp,0,sizeof(temp));
for(int i=1;i<=size;i++)
{
for(int j=1;j<=cnt;j++)
{
for(int k=1;k<=cnt;k++)
{
temp[i][j]+=arr1[i][k]*arr2[k][j]%mod;
temp[i][j]%=mod;
}
}
}
memcpy(arr1,temp,sizeof(temp));
}
void qpow(int t)
{
for(int i=1;i<=cnt;i++) res[i][i]=1;
while(t)
{
if(t&1) mulit(res,table2,cnt);
mulit(table2,table2,cnt);
t>>=1;
}
}

然而题目要求我们不能立刻走回头路,处理起来比较麻烦

所以我们把边当成点,把相连的边“连接

另外,为了区分出边的方向,我们把每条边拆成两条(分别两个方向)

然后按照上述的方法处理出$table2[t][i][j]$

最后枚举一遍从起点发出的边,统计这些边到达终点的方案数,输出即可

代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int table2[130][130],n,a,b,cnt,from[130],to[130],head[130],nxt[130];
int res[130][130];
#define connect(a,b) table2[a][b]=1
#define mod 45989
int temp[130][130];
void mulit(int arr1[][130],int arr2[][130],int size)
{
memset(temp,0,sizeof(temp));
for(int i=1;i<=size;i++)
{
for(int j=1;j<=cnt;j++)
{
for(int k=1;k<=cnt;k++)
{
temp[i][j]+=arr1[i][k]*arr2[k][j]%mod;
temp[i][j]%=mod;
}
}
}
memcpy(arr1,temp,sizeof(temp));
}
void qpow(int t)
{
for(int i=1;i<=cnt;i++) res[i][i]=1;
while(t)
{
if(t&1) mulit(res,table2,cnt);
mulit(table2,table2,cnt);
t>>=1;
}
}
void link(int x,int y)
{
from[++cnt]=x,to[cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}
int main()
{
int m,t;
cin>>n>>m>>t>>a>>b;
a++,b++;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
x++,y++;
link(x,y);
link(y,x);
}
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=cnt;j++)
{
if(((i&1)&&j==i+1)||((i&1)==0&&j==i-1)) continue;
if(to[i]==from[j]) connect(i,j);
//if(from[i]==to[j]) connect(j,i); }
}
/*for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=cnt;j++) cout<<table2[i][j]<<" ";
cout<<endl;
}*/
qpow(t-1);
int ans=0;
for(int l=head[a];l;l=nxt[l])
for(int i=1;i<=cnt;i++)
if(to[i]==b) ans+=res[l][i],ans%=mod;
cout<<ans;
}

  

[难题题解] [BZOJ1875] [SDOI2009] HH去散步的更多相关文章

  1. bzoj1875: [SDOI2009]HH去散步

    终于A了...早上按自己以前的写法一直WA.下午换了一种写法就A了qwq #include<cstdio> #include<cstring> #include<iost ...

  2. 【题解】 bzoj1875: [SDOI2009]HH去散步 (动态规划+矩阵乘法)

    bzoj1875,懒得复制,戳我戳我 Solution: 看到这道题,看的出是个dp,每个点\(t\)时刻到达的方案数等于\(t-1\)到连过来的点方案数之和 但又因为题目有要求不能走一样的边回去不是 ...

  3. BZOJ1875 [SDOI2009]HH去散步 矩阵

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1875 题意概括 在一个无向图(有重边无自环)中走,不能在经过连续经过某一条边2次. 现在走t步,问 ...

  4. BZOJ1875 [SDOI2009]HH去散步 【dp + 矩阵优化】

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

  5. bzoj1875 [SDOI2009]HH去散步 矩阵快速幂

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=1875 题解 如果没有这个"不能立刻沿着刚刚走来的路走回",那么这个题就是一 ...

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

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

  7. BZOJ1875: [SDOI2009]HH去散步 图上边矩乘

    这道题十分的坑…… 我作为一只连矩乘都不太会的渣渣看到这道题就只能神搜了….. 首先说一下普通的矩乘求方案,就是高出邻接矩阵然后一顿快速幂….. 矩乘一般就是一些秘制递推….. 再说一下这道题,我们可 ...

  8. bzoj1875 [SDOI2009]HH去散步——矩阵快速幂

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1875 有个限制是不能走回头路,比较麻烦: 所以把矩阵中的元素设成边的经过次数,单向边之间就好 ...

  9. bzoj 1875: [SDOI2009]HH去散步 -- 矩阵乘法

    1875: [SDOI2009]HH去散步 Time Limit: 20 Sec  Memory Limit: 64 MB Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走, ...

随机推荐

  1. Shell 脚本学习(1)

    一 Shell概览 1. 自动化批量系统初始化程序(update, 软件安装,时区设置,安全策略,...) 2. 自动化批量软件部署程序(LAMP,LNMP,Tomcat,LVS,Nginx) 3. ...

  2. Spring WebFlux 01 (原理及使用场景)

    一.什么是 Spring WebFlux 好多人以为Spring WebFlux就是Spring MVC的升级版,其实不然,那到底什么是Spring WebFlux呢,首先就要搞清楚Spring We ...

  3. Navicat15安装激活版教程

    navicat15安装 一键式安装,安装包如下 链接:https://pan.baidu.com/s/1VTJmJ7ulUySWoWBu-fugiw 提取码:fz5u 先安装软件包点击安装,一直下一步 ...

  4. max depth exceeded when dereferencing c0-param0的问题

    在做项目的时候,用到了dwr,有一次居然报错,错误是max depth exceeded when dereferencing c0-param0 上网查了一下,我居然传参数的时候传的是object类 ...

  5. 什么是jsp?

    1.什么是jsp? jsp就是java 服务器页面(java server page) 2.jsp有什么用? jsp的出现是为了解决Servlet页面显示方面的不足. 3.jsp的三种脚本: 4.js ...

  6. 关于soapui的使用

      打开SoapUI软件,点击File -->NewSoapProject 创建测试项目 输入测试项目名称,点击OK保存 在测试项目上右击选择AddWSDL 输入所需要测试的接口地址,点击ok确 ...

  7. 功能+自动化测试代码扫描(demo)

    Jacoco 是一个开源的覆盖率工具.Jacoco 可以嵌入到 Ant .Maven 中,并提供了 EclEmma Eclipse 插件,也可以使用 Java Agent 技术监控 Java 程序.很 ...

  8. 在页面制作的时候常用的html页面滚动加载,可视区域判断方法

    演示图 考虑2个情况一种情况初始状态下 滚动到在中间区域的时候,这时上半部分看不见的元素就不给字体添加红色一种情况是,从头向下看的. 代码 .ss li { margin: 40px; } <d ...

  9. python入门008

    目录 一.for循环 作用:for循环是因为在循环取值(即遍历值)时for循环比while循环的使用更为简洁 1.for循环语法: 2.应用案例: 注意:break 与 continue也可以用于fo ...

  10. day78 通过axios实现数据请求

    目录 一.axios提供http请求的两个常用方法 二.json 1.json数据的语法 2 js中提供json数据转化的方式 三 ajax 1 数据接口 2 ajax在jq和vue的不同使用 3 同 ...