分类
标签
2-SAT AC自动机 BFS CDQ dfs DP fail树 FFT FFT&NTT FWT hash KD-Tree KMP LCA SPFA STL Tarjan Treap Trie 主席树 乱搞 二分 二分图匹配 二分答案 二维SPFA 交互 位运算 其他 最小生成树 分块 区间DP 半平面交 博弈论 可持久化 可持久化Trie树 后缀数组 图库 平衡树 并查集 插头DP 数学 数论 无旋Treap 日记 暴力 权值树状数组 栈 树DP 树套树 树状数组 树贪心 概率DP 模拟 欧拉定理 点分治 状压DP 生成函数 矩阵乘 线性规划 线段树 组合 网络流 群论 莫比乌斯反演 计算几何 贪心 费用流 高斯消元
781 字
4 分钟
BZOJ 1875 [SDOI2009]HH去散步 矩阵乘
Made by WZZ
Description
HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每 天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。 现在给你学校的地图(假设每条路的长度都 是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径
Input
第一行:五个整数N,M,t,A,B。
N表示学校里的路口的个数
M表示学校里的 路的条数
t表示HH想要散步的距离
A表示散步的出发点
B则表示散步的终点。
接下来M行
每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。
数据保证Ai != Bi,但不保证任意两个路口之间至多只有一条路相连接。
路口编号从0到N -1。
同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。
答案模45989。
N ≤ 20,M ≤ 60,t ≤ 2^30,0 ≤ A,B
Output
一行,表示答案。
Sample Input
4 5 3 0 0
0 1
0 2
0 3
2 1
3 2
Sample Output
4
这道题是一道矩阵乘的题
通常我们用矩阵乘表示 点a 到 点b
1 2 3 4 …N-1 N 1-1 1-2 … N-1
2-1 2-2 … N-2
3-1 3-2 … N-3
… … … …
N-1 N-2 … N-N
乘之后便为到 i点的方案数
但这道题有不能重复走边的限制
我们便巧妙地用矩阵储存边
边 i 能到达 边 j的条件为 edge[i].to==edge[j].from(即相连)
但 i不能到达i+1条边(i为偶数) 所以a[i][i+1]=0;
再循环找满足条件即可
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define mod 45989
using namespace std;
int n, m, t, size, head[25], began, endd, x, y;
int a[160][160], ans[160][160], tot;
struct node
{
int from, to, next;
} edge[400];
void add(int from, int to)
{
edge[++size].from = from;
edge[size].to = to;
edge[size].next = head[from];
head[from] = size;
}
void cheng(int a[160][160], int b[160][160], int to[160][160])
{
int tmp[160][160] = {0};
for (int i = 1; i <= size; i++)
for (int j = 1; j <= size; j++)
for (int u = 1; u <= size; u++)
tmp[i][j] = (tmp[i][j] + a[i][u] * b[u][j] % mod) % mod;
for (int i = 1; i <= size; i++)
for (int j = 1; j <= size; j++)
to[i][j] = tmp[i][j];
}
void dp()
{
while (t)
{
if (t & 1)
cheng(a, ans, ans);
t = t >> 1;
cheng(a, a, a);
}
for (int i = 1; i <= size; i++)
if (edge[i].to == endd)
for (int j = 1; j <= size; j++)
if (edge[j].from == began)
tot = (tot + ans[j][i]) % mod;
}
int main()
{
scanf("%d%d%d%d%d", &n, &m, &t, &began, &endd);
began++, endd++;
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
x++, y++;
add(x, y);
add(y, x);
}
for (int i = 1; i <= size; i++)
for (int j = 1; j <= size; j++)
if (edge[i].to == edge[j].from)
a[i][j] = 1;
for (int i = 1; i <= size; i += 2)
a[i][i + 1] = a[i + 1][i] = 0;
for (int i = 1; i <= size; i++)
ans[i][i] = 1;
t--;
dp();
printf("%d", tot);
}
BZOJ 1875 [SDOI2009]HH去散步 矩阵乘
https://www.nekomio.com/posts/35/