【矩阵乘法】【倍增】WYC(luogu 3597)

WYC

题目大意

给你一个有向图,让你求图中的 k k k短路(非简单路径)

输入样例#

6 6 11
1 2 1
2 3 2
3 4 2
4 5 1
5 3 1
4 6 3

输出样例#1

4

数据范围

1 ⩽ n ⩽ 40 , 1 ⩽ m ⩽ 1000 , 1 ⩽ k ⩽ 1 0 18 1\leqslant n\leqslant 40,1\leqslant m\leqslant 1000,1\leqslant k\leqslant 10^{18} 1n401m10001k1018
1 ⩽ u , v ⩽ n , u ≠ v , 1 ⩽ c ⩽ 3 1\leqslant u,v\leqslant n,u\neq v,1\leqslant c\leqslant 3 1u,vnu=v1c3

解题思路

边长和点数很小,我们可以考虑拆点 + + +矩阵乘法
我们得出 f i = A 2 i f_i=A^{2^i} fi=A2i
我们加一个计数点,所有整点都连向它,它再连一个自环
这样0就可以计算长度小于i的路线会保留下来
我们再倍增判断求k短路

代码

#include
#include
#include
#include
#define ll long long
#define p(x, y) ((x) * 3 + (y) + 1)
using namespace std;
ll n, m, k, x, y, z, w, nn, ans, pw[100];
struct matrix
{
	ll a[150][150];
}f[100], g, gg;
matrix jc(matrix &a, matrix &b)
{
	matrix c;
	memset(c.a, 0, sizeof(c.a));
	for (ll i = 0; i < nn; ++i)
		for (ll j = 0; j < nn; ++j)
			for (ll k = 0; k < nn; ++k)
				c.a[i][j] += a.a[i][k] * b.a[k][j];//矩阵乘法
	return c;
}
bool check(matrix a)
{
	ll sum = 0;
	for (ll i = 0; i < n; ++i)
	{
		sum += a.a[p(i, 0)][0] - 1;//因为每个点都有一种方案直接到0,无边
		if (k <= sum) return 1;
	}
	return 0;
}
int main()
{
	scanf("%lld%lld%lld", &n, &m, &k);
	nn = n * 3 + 1;
	f[0].a[0][0] = 1;
	for (ll i = 0; i < n; ++i)
	{
		f[0].a[p(i, 2)][p(i, 1)] = 1;//拆点
		f[0].a[p(i, 1)][p(i, 0)] = 1;
		f[0].a[p(i, 0)][0] = 1;
		gg.a[p(i, 0)][p(i, 0)] = 1;
	}
	for (ll i = 1; i <= m; ++i)
	{
		scanf("%lld%lld%lld", &x, &y, &z);
		x--;
		y--;
		f[0].a[p(x, 0)][p(y, z - 1)]++;
	}
	pw[0] = 1;
	while(++w)
	{
		if (w > 60)
		{
			printf("-1");
			return 0;
		}
		pw[w] = pw[w - 1] * 2;
		f[w] = jc(f[w - 1], f[w - 1]);//计算f
		if (check(f[w])) break;
	}
	while(w--)
	{
		g = jc(gg, f[w]);
		if (!check(g))//判断当前是否在k短路内
			ans += pw[w], gg = g;//计算结果
	}
	printf("%lld", ans);
	return 0;
}

你可能感兴趣的:(矩阵乘法,倍增)