洛谷 - P6190

SmileMask發表於2024-07-15

題目大意

給定 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;
}