洛谷 - P6190

题目大意

给定 n点m边带权有向图,从 1 到 \(n\) 的路径中,经过一条边时可让其权值变为相反数,再变为原权值,求路径最小权值。

分析

先用 \(Floyd\) 求出全源最短路。
借用 \(Floyd\) 数组列出 \(dp\) 状态,\(f_{i,j}\) 表示从 \(i\)\(j\) 的最短路权值。
但似乎进行不下去了,我们不妨将边反转的数量加入 \(dp\) 状态中。
状态变为 \(f_{k,i,j}\) 表示用了 从 \(i\)\(j\) 的路径上反转了 \(k\) 次的权值。
发现转移方程大概可以写成这样 $$dp_{i,x,k_1}+dp_{y,j,k_2}-w(x,y) \rightarrow dp_{i,j,k_1+k_2+1}$$
发现这十分类似与快速幂的形式!而且 \(max,+\) 具有结合律。
我们成功找到解题关键,关于图论题目可以用矩阵快速幂解决。
现在问题转化为了如何构造矩阵。
考虑在求全源最短路的数组上,发现基础矩阵即为 \(f_{1,i,j}\)!可直接 \(O(mn^2)\)求出,接下来进行广义矩阵快速幂即可,复杂度为 \(O(n^3logk)\)
可以通过此题。

代码

const int N=101,M=2501;
int n,m,k;
int u[M],v[M],w[M];
struct node{
	int a[N][N];
	node(){memset(a,0x3f,sizeof(a));}
	friend node operator *(node x,node y){
		node z;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				for(int k=1;k<=n;k++)
				z.a[i][j]=min(z.a[i][j],x.a[i][k]+y.a[k][j]);
		return z;
	}	
}a,b;
node power(node x,int y){
	node ans=a;
	while(y){
		if(y&1) ans=ans*x;
		x=x*x;y>>=1;
	}
	return ans;
}
signed main(){
	n=rd(),m=rd(),k=rd();
	for(int i=1;i<=m;i++){
		u[i]=rd(),v[i]=rd(),w[i]=rd();
		a.a[u[i]][v[i]]=w[i];
	}
	for(int i=0;i<=n;i++) a.a[i][i]=0;
	for(int t=1;t<=n;t++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				a.a[i][j]=min(a.a[i][j],a.a[i][t]+a.a[t][j]);
	if(!k) return cout<<a.a[1][n],0;
	for(int t=1;t<=m;t++){
		int val=w[t];
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				b.a[i][j]=min(b.a[i][j],min(a.a[i][j],a.a[i][u[t]]+a.a[v[t]][j]-val));
			}
		}
	}
	cout<<power(b,k).a[1][n];
	return 0;
}
posted @ 2024-07-15 21:03  SmileMask  阅读(8)  评论(0编辑  收藏  举报