Title
CF147B Smile House
Solution
设 d [ s ] [ i ] [ j ] d[s][i][j] d[s][i][j]为 d d d步从 i i i到 j j j的距离, d [ s ] [ i ] [ j ] = m a x ( d [ s ] [ i ] [ j ] , d [ s − 1 ] [ i ] [ k ] + d [ 1 ] [ k ] [ j ] ) d[s][i][j]=max(d[s][i][j],d[s-1][i][k]+d[1][k][j]) d[s][i][j]=max(d[s][i][j],d[s−1][i][k]+d[1][k][j]),若 d [ k ] [ i ] [ i ] > 0 d[k][i][i]>0 d[k][i][i]>0则 k k k是答案。 这是 O ( n 4 ) O(n^4) O(n4),能拿70分。
我们可以用倍增优化成 O ( n 3 l o g n ) O(n^3logn) O(n3logn)
设 d [ s ] [ i ] [ j ] d[s][i][j] d[s][i][j]为不超过 2 s 2^s 2s步从 i i i到 j j j的距离, d [ s ] [ i ] [ j ] = m a x ( d [ s ] [ i ] [ j ] , d [ s − 1 ] [ i ] [ k ] + d [ s − 1 ] [ k ] [ j ] ) d[s][i][j]=max(d[s][i][j],d[s-1][i][k]+d[s-1][k][j]) d[s][i][j]=max(d[s][i][j],d[s−1][i][k]+d[s−1][k][j])
像倍增求 l c a lca lca一样,从大到小枚举 s s s
如果当前存在正环,就继续枚举更小的可行解,否则累加 2 s − 1 2^{s-1} 2s−1,这种情况下需要保留上一次的最长路径。
对于答案需要++,因为自己这个点没有累加。
Code
#include
#include
#include
#define rep(i,x,y) for(register int i=x;i<=y;i++)
using namespace std;
const int N=305;
const int M=10;
int n,m,d[M+5][N][N],q[N][N],t[N][N],ans;
int main(){
memset(d,0xcf,sizeof(d));
memset(q,0xcf,sizeof(q));
scanf("%d%d",&n,&m);
rep(i,1,m){
int x,y,z,zz;
scanf("%d%d%d%d",&x,&y,&z,&zz);
d[0][x][y]=z; d[0][y][x]=zz;
}
rep(i,1,n) d[0][i][i]=q[i][i]=0;
rep(s,1,M) rep(k,1,n) rep(i,1,n) rep(j,1,n) d[s][i][j]=max(d[s][i][j],d[s-1][i][k]+d[s-1][k][j]);
for(int s=M;s>=0;s--){
memset(t,0xcf,sizeof(t)); bool b=0;
rep(k,1,n) rep(i,1,n) rep(j,1,n) t[i][j]=max(t[i][j],q[i][k]+d[s][k][j]);
rep(i,1,n) if (t[i][i]>0) {b=1; break;}
if (b) continue; else {
rep(i,1,n) rep(j,1,n) q[i][j]=t[i][j];
ans+=(1<<s);
}
}
printf("%d",ans>=n?0:ans+1);
return 0;
}