P6190 [NOI Online #1 入门组]魔法 |矩阵加速
题目描述
C 国由 \(n\) 座城市与 \(m\) 条有向道路组成,城市与道路都从 \(1\) 开始编号,经过 \(i\) 号道路需要 \(t_i\) 的费用。
现在你要从 \(1\) 号城市出发去 \(n\) 号城市,你可以施展最多 \(k\) 次魔法,使得通过下一条道路时,需要的费用变为原来的相反数,即费用从 \(t_i\) 变为 \(-t_i\)。请你算一算,你至少要花费多少费用才能完成这次旅程。
注意:使用魔法只是改变一次的花费,而不改变一条道路自身的 \(t_i\);最终的费用可以为负,并且一个城市可以经过多次(包括 \(n\) 号城市)。
输入格式
输入的第一行有三个整数,分别代表城市数 \(n\),道路数 \(m\) 和魔法次数限制 \(k\)。
第 \(2\) 到第 \((m + 1)\) 行,每行三个整数。第 \((i + 1)\) 行的整数 \(u_i, v_i, t_i\) 表示存在一条从 \(u_i\) 到 \(v_i\) 的单向道路,经过它需要花费 \(t_i\)。
输出格式
输出一行一个整数表示最小的花费。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=105,mod=2017;
#define int long long
int f[N][N],n,m,K;
struct node{
int u,v,w;
}e[N*N];
struct mat{
int t[N][N];
void clear(){
memset(t,63,sizeof(t));
}
}a;
inline mat C(mat x,mat y){
mat t; t.clear();
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
t.t[i][j]=min(t.t[i][j],x.t[i][k]+y.t[k][j]);
return t;
}
inline mat ksm(mat x,int y){
mat res;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
res.t[i][j]=f[i][j];
while(y){
if(y&1)res=C(res,x);
x=C(x,x); y>>=1;
}
return res;
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&K);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)f[i][i]=0;
for(int i=1,u,v,w;i<=m;i++){
scanf("%lld%lld%lld",&u,&v,&w);
f[u][v]=w;
e[i]=(node){u,v,w};
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
a.clear();
for(int k=1;k<=m;k++){
int u=e[k].u,v=e[k].v,w=e[k].w;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a.t[i][j]=min(a.t[i][j],min(f[i][j],f[i][u]+f[v][j]-w));
}
if(K==0)printf("%lld\n",f[1][n]);
else printf("%lld\n",ksm(a,K).t[1][n]);
}
不以物喜,不以己悲