其他分享
首页 > 其他分享> > 【luogu P2151】HH去散步(DP)(矩阵乘法)

【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