【Luogu P2886】[USACO07NOV]奶牛接力

题目链接:

题目

题目大意:

给出一张无向连通图,求 \(S\)\(E\) 经过 \(k\) 条边的最短路。

正文:

题目中 \(u,v\leq 1000\),但是题目里的图是无向连通图,而 \(T\leq100\),也就是说,我们可以先离散化一下方便计算和优化空间复杂度。

而问题中要求的是最短路,我们可以定义广义矩阵乘法 \(A\times B=C\)

\[C_{i,j}=\min_{k=1}^{n}(A_{i,k}+B_{k,j}) \]

你会发现这其实是个 Floyd。接着用这个广义矩阵乘法来快速幂 \(k-1\) 次题目中的邻接矩阵就能得到答案了。

代码:

struct matrix
{
	ll mat[N][N];
	int n, m;
	matrix(){memset(mat, 127 / 3, sizeof mat);}
	inline ll* operator [] (int b) { return mat[b];}
}f;

inline matrix operator*(matrix &a, matrix &b)
{
	matrix c; c.n = a.n, c.m = b.m;
	for (int k = 1; k <= a.m; k++)
		for (int i = 1; i <= a.n; i++)
			for (int j = 1; j <= b.m; j++)
				c[i][j] = min(c[i][j] , a[i][k] + b[k][j]);
	return c;
}

matrix qpow(matrix a, ll b)
{
	b--;
	matrix ans; 
	ans = a;
	for (; b; b >>= 1)
	{
		if(b & 1) ans = ans * a; 
		a = a * a;
	}
	return ans;
}

int bucket[N * 10], cnt;

int main()
{
	scanf ("%d%d%d%d", &k, &m, &s, &t);
	for (int i = 1; i <= m; i++)
	{
		int u, v, w;
		scanf ("%d%d%d", &w, &u, &v);
		if(!bucket[u]) bucket[u] = ++cnt;
		if(!bucket[v]) bucket[v] = ++cnt;
		f[bucket[u]][bucket[v]] = f[bucket[v]][bucket[u]] = w;
	}
	f.n = f.m = cnt;
	f = qpow(f, k);
	printf ("%d\n", f[bucket[s]][bucket[t]]);
	return 0;
}
posted @ 2020-12-12 15:21  Jayun  阅读(65)  评论(0编辑  收藏  举报