题解 P8017 [COCI2013-2014#4] UTRKA
洛谷。
题意
这个原题描述应该足够理解。
分析
首先可以把 $M_i$ 和 $S_i$ 缩去,令 $w_i=S_i-M_i$,那么我们最后题目就变成了求一条首尾相同的路径权值和大于 $0$,其路径数最小并且在路径数相同情况下权值和最大。(可以重复经过)。
这有点像我们的定长路径的最短路,那么我们用矩阵来维护我们的某个点开始的 $k$ 步的最长路径长度。
先从暴力打起,确定我们的起点,再枚举路径数,看看此时到自己的最长路径长度是否大于 $0$,如果是,就更新答案,并且结束。
inline void solve(int st) {
ans=<%1,n%>;
for(int i=1; i<=n; ++i) ans.a[1][i]=-INF;
ans.a[1][st]=0;
for(int i=1; i<=t; ++i) {//小优化,初始值为 m
ans=ans*bas;
if(ans.a[1][st]>0) {
if(t>i) t=i,res=ans.a[1][st];
if(t==i) res=max(res,ans.a[1][st]);
return;
}
}
}
就是这样简单的思路,我们就先拿下了 $118pts$。
时间复杂度:$O(n^4\times t)$。
接下来有两种优化方向,我们都需要运用。
- 优化枚举起点,这里占据了 $O(n)$ 的时间复杂度。
- 优化取得答案,这里占据了 $O(t)$ 的时间复杂度。
优化第一个。
我们其实并不需要枚举起点,每个起点他们的所乘 $bas$ 都是一样的,我们尝试把求第 $i$ 个开始最后到达自己的最长路径融入 $bas$。
在起始时,我们将所有的 $bas_{i,i}$ 设为 $0$,那么,我们就相当于把所有的 $ans$ 融入了我们的 $bas$,而这样对我们的答案转移是否有影响呢?
显然是没有的,应为我们要求的是最长路径长度,我们多走一条长度为 $0$ 的自环,并没有增长长度。
可以再次拿下一个点。
优化第二个。
在不优化第一个的前提下,我们同样也是可以优化的。
就像我们的第一个优化,给 $st$ 连一个自环,那么我们最后求出的就是至多 $k$ 条边的最长路径长度,显然,这是满足二分性质的,我们二分套一个矩阵快速幂即可。(但是好像两个 $\log$ 还变慢了)。
再其基础上,我们可以发现,我们第一个优化后,也是满足二分性质的。
但是还可以更进一步,因为我们二分后是有一个快速幂操作的,快速幂本质上其实与倍增无异,我们单用倍增优化掉这一个二分。
时间复杂度:$O(n^3 \times \log m )$。
这个时间复杂度其实已经足够度过这道题了,但是我们可以更快一点点。
分析一下答案的范围,我们答案的范围是多少,之前我们用最短路的一个最值,也就是我们的边数,但是我们的答案范围其实只有 $n$。
为什么呢,假如说大于了 $n$,那么就代表,我们必然经过了一个环,而且这个环还一定是一个正环,否则我们并不用经过这个环。而又因为这个环是正环,所以我们从环上去一个节点作为起点,而这个节点在跑完环是也是正值,因而答案不超过 $n$。
时间复杂度:$O(n^3\times \log n)$。
n=read(),m=read();
bas=<%n,n%>;
for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j) bas.a[i][j]=-INF;
for(int i=1; i<=n; ++i) bas.a[i][i]=0;
for(int i=1; i<=m; ++i) {
int u=read(),v=read(),mm=read(),ss=read();
bas.a[u][v]=max(ss-mm,bas.a[u][v]);
}
pw[0]=bas;
for(int i=1; i<=M; ++i) pw[i]=pw[i-1]*pw[i-1];
ans=bas;
int tot=1;
for(int i=M; ~i; --i) {
MAT res=ans*pw[i];
if(!res.check()) tot+=(1<<i),ans=res;
}
++tot,ans=ans*bas;
cout<<tot<<" "<<ans.get();//get:获取最大值
return 0;

浙公网安备 33010602011771号