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]);
}
posted @ 2020-05-28 17:27  白木偶君  阅读(155)  评论(0编辑  收藏  举报