P6190 [NOI Online 入门组]魔法
P6190 [NOI Online 入门组]魔法
题目描述
分析
这道题要求的是从1到\(n\)的最短路,其中有\(k\)次机会可以将一条边暂时的变为相反数,一条边可以走无数次
注意到无数次这个比较奇怪的要求,也就是说可以不考虑重复,那么设\(a_{k,i,j}\)表示从\(i\)到\(j\)用\(k\)次魔法的最小值,那么显然\(a_{k,i,j}=min(a_{u,i,v}+a_{k-u,v,j})\),即把\(i\)到\(j\)分为两段,那么首先对于\(k=0\)的情况,可以直接Floyd解决,对于\(k=1\)可以枚举所有的边解决,然后我们就得到了一个矩阵M,我们显然可以定义一种矩阵乘法\(a*b=min(a_{i,u}+b_{u,j})\)符合我们上面推出来的转移方程
那么对于\(k\)的情况,即用矩阵快速幂求出\(M^k\)即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N=105;
struct Node
{
long long matrix[N][N];
}a,b;
int n,m,k,edge[2505][3];
void Input()
{
scanf("%d%d%d",&n,&m,&k);
memset(a.matrix,63,sizeof(a.matrix));
memset(b.matrix,63,sizeof(b.matrix));
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a.matrix[x][y]=z;
edge[i][0]=x; edge[i][1]=y; edge[i][2]=z;
}
return;
}
void Prepare()
{
for(int i=1;i<=n;i++) a.matrix[i][i]=0;
for(int kk=1;kk<=n;kk++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
// if( i!=j && i!=kk && j!=kk )
a.matrix[i][j]=min(a.matrix[i][j],a.matrix[i][kk]+a.matrix[kk][j]);
// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=n;j++) cout<<a.matrix[i][j]<<" ";
// cout<<endl;
// }
for(int i=1;i<=n;i++) b.matrix[i][i]=0;
for(int kk=1;kk<=m;kk++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
b.matrix[i][j]=min(b.matrix[i][j],min(a.matrix[i][j],a.matrix[i][edge[kk][0]]+a.matrix[edge[kk][1]][j]-edge[kk][2]));
// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=n;j++) cout<<b.matrix[i][j]<<" ";
// cout<<endl;
// }
return;
}
Node matrix_mul(Node A,Node B)
{
Node tem;
memset(tem.matrix,63,sizeof(tem.matrix));
for(int kk=1;kk<=n;kk++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
tem.matrix[i][j]=min(tem.matrix[i][j],A.matrix[i][kk]+B.matrix[kk][j]);
return tem;
}
long long matrix_pow(Node x,int y)
{
Node tem=a;
while( y )
{
if( y%2 ) tem=matrix_mul(x,tem);
x=matrix_mul(x,x);
y/=2;
}
return tem.matrix[1][n];
}
void work()
{
if( k==0 ) printf("%lld",a.matrix[1][n]);
else printf("%lld",matrix_pow(b,k));
return;
}
int main()
{
Input();
Prepare();
work();
return 0;
}