COCI 2013/2014 #4 UTRKA 矩阵快速幂
COCI 2013/2014 #4 UTRKA 矩阵快速幂
首先考虑一条路线,设 \(M\) 为 Mirko 需要的时间,而 \(S\) 为 Slavko 需要的时间,那么 \(-(M-S)\) 就是 Mirko 比 Slavko 快的时间。那么我们将边权转化一下,第 \(i\) 条边的边权为 \(M_i-S_i\)。 如果忽略边数的要求,那么答案就是从一个点出发再回到这个点的最短路。注意到,我们的答案是具有单调性的,如果 \(a\) 条边的路线可以,那么 \(b(b>a)\) 条边的路线也可以,只要我们在上一条路线的基础上换个起点,保证多走的边权和为负值就行。既然答案具有单调性,我们考虑二分。但是这题二分并不好处理走 \(k\) 条边的最短路,如果想要处理,我们得再套一个倍增。而实际上,既然运用到倍增,我们反而可以不用二分了。
我们设 \(t_k\) 为一个矩阵,其中 \(t_{k,i,j}\) 表示走 \(2^k\) 条边从 \(i\) 走到 \(j\) 的最短路。那么 \(t_0\) 就是我们输入获得的邻接矩阵,然后我们定义矩阵乘法的运算:如果 \(c=a\times b\),那么
本质上就是跑了个 Floyd,用 Floyd 来定义矩阵乘法的原因就是为了要构造出一种能通过 \(t_i\) 推出 \(t_{i+1}\) 的方式。那么就有递推式 \(t_i=t_{i-1}\times t_{i-1}\)。
设 \(res\) 为现在得到的矩阵,\(cnt\) 为最短的边数。本题要我们求最短的且 Mirko 比 Slavko 快的路线,那么我们求出最长的且 Mirko 比 Slavko 慢的路线,然后对于得到的矩阵 \(res\) 再乘上一个 \(t_0\) 取极值得到 Mirko 比 Slavko 最快快多少,最短的边数就是 \(cnt+1\)。
那么我们像求 lca 一样从高位往低位枚举,,如果 \(res\times t_i\) 这个矩阵满足从任意一个点出发再回到这个点 Mirko 比 Slavko 慢的话,就让 \(res=res\times t_i,cnt=cnt+2^i\) 。
代码如下:
#include<bits/stdc++.h>
#define R register
#define ll long long
using namespace std;
const int MAXN = 305;
bool f1;
int n,m;
struct matrix
{
ll dis[MAXN][MAXN];
matrix(){for(int i=1;i<=300;++i) for(int j=1;j<=300;++j)dis[i][j]=1e15;}
matrix operator * (const matrix&a) const
{
matrix ans;
for(R int k=1;k<=n;++k) for(R int i=1;i<=n;++i) for(R int j=1;j<=n;++j)
ans.dis[i][j]=min(ans.dis[i][j],dis[i][k]+a.dis[k][j]);
return ans;
}
}t[22],tmp;
bool f2;
bool check()
{
for(R int i=1;i<=n;++i)
if(tmp.dis[i][i]<0) return true;
return false;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;++i)
{
int a,b,s,m;
scanf("%d %d %d %d",&a,&b,&m,&s);
t[0].dis[a][b]=m-s;
}
for(int i=1;i<=n;++i)
t[0].dis[i][i]=0;
for(int i=1;i<=20;++i) t[i]=t[i-1]*t[i-1];
matrix res;bool flag=0;//因为懒得算单位矩阵,就开一个bool变量记res是否更新过
int ans=0;
for(int i=20;i>=0;--i)
{
if(!flag) tmp=t[i];
else tmp=res*t[i];
if(!check())
{
flag=1;
res=tmp,ans+=1<<i;
}
}
if(flag) res=res*t[0];//记得对目标矩阵和答案都+1
else res=t[0];
++ans;
ll minn=0;
for(R int i=1;i<=n;++i)
minn=min(res.dis[i][i],minn);
printf("%d %lld\n",ans,-minn);
return 0;
}