[NOI Online 2020 #1]魔法 题解
题意简述
给定一个 \(n\) 个点 \(m\) 条边的带权有向图,你可以进行至多 \(k\) 次操作,使得下一次通过路径的权值变为其相反数,之后再变回来。
问从 \(1\) 号点到 \(n\) 号点的最短路。
\(n≤100,m≤2500,k≤10^6\)。
Solution
先考虑 \(70\) 分怎么做:
\(k=0\) 时直接跑个 \(floyd\) 就好了。
\(k=1\) 时也很简单,枚举对哪条边使用魔法。
记 \(f(k,i,j)\) 为进行至多 \(k\) 次操作,从 \(i\) 到 \(j\) 的最短路,有方程 \(f(1,i,j)=min \left\{f(0,i,e_u)+f(0,e_v,j)-e_t\right\}\)。
拓展到 \(k\) 较大时的情况,有 \(f(k,i,j)=min \left\{f(x,i,p)+f(k-x,p,j)\right\} \left(0<x<k\right)\)。
直接转移是 \(O(n^3k)\) 的,显然无法通过。
考虑优化,发现这个转移方程的形式就非常得好,它是一个广义矩阵乘法,满足结合律,于是我们可以直接对其矩阵快速幂。
时间复杂度 \(O(n^3logk+ n^2m)\)
在 \(NOI2020\) 的 \(D1T1\) 也有用到这个东西,还是蛮重要的。
记得特判 \(k=0\)。
Code
#include<bits/stdc++.h>
#define IL inline
#define RE register
#define ll long long
#define N 110
#define M 2550
#define INF (1ll<<60)
IL int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+(ch^48),ch=getchar();
	return x*f;
}
int n,m,k;
template<class T1,class T2>IL void cmin(T1 &x,T2 y){x=x<y?x:y;}
struct Matrix
{
	ll a[N][N];
	Matrix(){memset(a,63,sizeof(a));}
	void copy(ll b[][N]){for(RE int i=1;i<=n;++i)for(RE int j=1;j<=n;++j)cmin(a[i][j],b[i][j]);}
	Matrix operator *(const Matrix &x)const
	{
		Matrix ret;
		for(RE int i=1;i<=n;++i)
			for(RE int j=1;j<=n;++j)
				for(RE int k=1;k<=n;++k)
					cmin(ret.a[i][j],a[i][k]+x.a[k][j]);
		return ret;
	}
}base,ans;
struct Edge{int u,v,t;}e[M];
ll dis[N][N];
IL void floyd()
{
	for(RE int k=1;k<=n;++k)
		for(RE int i=1;i<=n;++i)
			for(RE int j=1;j<=n;++j)
				cmin(dis[i][j],dis[i][k]+dis[k][j]);
}
int main()
{
	n=read(),m=read(),k=read();
	memset(dis,63,sizeof(dis));	
	for(RE int i=1;i<=n;++i)dis[i][i]=0;
	for(RE int i=1;i<=m;++i)
	{
		e[i]=(Edge){read(),read(),read()};
		dis[e[i].u][e[i].v]=e[i].t;
	}
	floyd();	
	if(!k)return printf("%lld",dis[1][n]),0;
	for(RE int i=1;i<=m;++i)
		for(RE int x=1;x<=n;++x)
			for(RE int y=1;y<=n;++y)
				cmin(base.a[x][y],dis[x][e[i].u]+dis[e[i].v][y]-e[i].t);
	base.copy(dis),ans.copy(dis);
	for(;k;k>>=1,base=base*base)if(k&1)ans=ans*base;
	printf("%lld",ans.a[1][n]);
	return 0;
}
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号