【luogu P2151】HH去散步(DP)(矩阵乘法)
作者:互联网
HH去散步
题目链接:luogu P2151
题目大意
给你一个无向图,然后给你起点终点走的路径数量,然后每次走不能走那条边的回头路,然后问你有多少走的方案。
思路
首先胡一下正常的做法吧。(乐)
就如果可以回头路那就简单,但是不能回头,考虑把它区分开来。
亦或者说你可以视作把边看做有向,然后再看做点,那双向边之间不能连边,就可以正常跑矩阵乘法啦。
这里给的是一种跟 \(m\) 无关的做法。
设 \(f_{x,y,z}\) 为最后到的两个点是 \(x,y\),走了 \(z\) 步的方案。(这样你就可以防止走回头路)
\(f_{x,y,z}=g_{x,y}\sum\limits_{i=1}^nf_{i,x,z-1}-f_{y,x,z-1}\)
但是矩阵边长为 \(n^2\) 不行(亦或者说不够好,在某个地方不太行)
考虑你答案要求什么(\(\sum\limits_{i=1}^nf_{i,T,L}\))
\(g1_{x,z}=\sum\limits_{i=1}^nf_{x,i,z}=\sum\limits_{i=1}^n(g_{x,i}\sum\limits_{j=1}^nf_{j,x,z-1}-f_{i,x,z-1})=\sum\limits_{i=1}^ng_{x,i}g2_{x,z-1}-g2_{x,z-1}=g2_{x,z-1}(\sum\limits_{i=1}^ng_{x,i}-1)\)
\(g2_{y,z}=\sum\limits_{i=1}^nf_{i,y,z}=\sum\limits_{i=1}^n(g_{i,y}\sum\limits_{j=1}^nf_{j,i,z-1}-f_{y,i,z-1})=\sum\limits_{i=1}^ng_{x,i}g2_{i,z-1}-g1_{y,z-1}\)
然后弄出这两个东西,你发现你可以只保留这两个的信息做矩阵乘法,然后矩阵边长就变成 \(2n\) 就可以过啦!
然后如果多组询问不同 \(L,T\),那就把快速幂改成倍增,也是一样的道理。
代码
#include<cstdio>
#include<cstring>
#define ll long long
//#define mo 998244353
#define mo 45989
using namespace std;
const int N = 105;
int n, m, Q, S, T, g[N][N];
int st[N << 1], jump[60][N << 1][N << 1], ans[N << 1], tmp[N << 1];
ll L;
int add(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int mul(int x, int y) {return 1ll * x * y % mo;}
void Init() {
for (int i = 1; i <= n; i++) st[S] = add(st[S], g[S][i]);
for (int i = 1; i <= n; i++) st[n + i] = add(st[n + i], g[S][i]);
for (int i = 1; i <= n; i++) {//g1
int tmp = mo - 1; for (int j = 1; j <= n; j++) tmp = add(tmp, g[i][j]);
jump[0][n + i][i] = tmp;
}
for (int i = 1; i <= n; i++) {//g2
for (int j = 1; j <= n; j++) jump[0][n + j][n + i] = add(jump[0][n + j][n + i], g[i][j]);
jump[0][i][n + i] = add(jump[0][i][n + i], mo - 1);
}
for (int z = 1; z < 60; z++) {
for (int k = 1; k <= (n << 1); k++)
for (int i = 1; i <= (n << 1); i++)
for (int j = 1; j <= (n << 1); j++)
jump[z][i][j] = add(jump[z][i][j], mul(jump[z - 1][i][k], jump[z - 1][k][j]));
}
}
int main() {
// freopen("ancient.in", "r", stdin);
// freopen("ancient.out", "w", stdout);
// scanf("%d %d %d %d", &n, &m, &Q, &S);
scanf("%d %d %d %d %d", &n, &m, &L, &S, &T); S++; T++;
for (int i = 1; i <= m; i++) {
int x, y; scanf("%d %d", &x, &y);
x++; y++;
g[x][y]++; g[y][x]++;
}
Init();
// while (Q--) {
// scanf("%d %lld", &T, &L);
// if (!L) {printf("%d\n", (S == T) ? 1 : 0); continue;}
if (!L) {printf("%d\n", (S == T) ? 1 : 0); return 0;}
L--; memcpy(ans, st, sizeof(ans));
for (int z = 59; z >= 0; z--) if ((L >> z) & 1) {
for (int k = 1; k <= (n << 1); k++)
for (int j = 1; j <= (n << 1); j++)
tmp[j] = add(tmp[j], mul(ans[k], jump[z][k][j]));
memcpy(ans, tmp, sizeof(ans)); memset(tmp, 0, sizeof(tmp));
}
printf("%d\n", ans[n + T]);
// }
return 0;
}
标签:P2151,g2,limits,int,luogu,sum,nf,HH,mo 来源: https://www.cnblogs.com/Sakura-TJH/p/luogu_P2151.html