ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【luogu P2151】HH去散步(DP)(矩阵乘法)

2022-07-15 22:03:03  阅读:166  来源: 互联网

标签:P2151 g2 limits int luogu sum nf HH mo


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有