LOJ#6075. 「2017 山东一轮集训 Day6」重建
题目描述:
给定一个 n个点m 条边的带权无向连通图 ,以及一个大小为k 的关键点集合S 。有个人要从点s走到点t,现在可以对所有边加上一个非负整数a,问最大的a,使得加上a后,满足:s到t的最短路长度=s到t且只能经过S中的点的最短路长度。
题目分析:
暴力
记x为只经过关键点的最短路长度,其路径条数为n
记y为可经过任意点的最短路长度,其路径条数为m
tip:路径条数意思这里指 覆盖最短路的边数
显然全部加上a之后最短路是不变的
也就是说我们要求这个东西
$$a*n+x=a*m+y$$
$$a=(\frac{y-x}{n-m})_{max}$$
所以我们怎么求这个东西呢
当然是D(bao)P(li)咯
假设f[i,j],g[i,j]表示到达i这个点,走过了j条边的最短路径
其中f[i,j]只经过关键点,g[i,j]经过任意一点,这两个数组暴力DP就能求出来
然后取出最大的a就好了
关于无解的情况:上面那个式子的值<0显然是不行的,m=n显然是取任意的(当然实现的话就不用这样了判断,这样讲只是方便理解)
代码:
1 //许多大佬貌似喜欢在代码上面写自己id,那我这个蒟蒻也就从此开始——跟啦! 2 //好吧Koko不是重点 3 //LevenKoko 4 #pragma GCC optimize("Ofast") 5 #include<bits/stdc++.h> 6 #define int long long 7 const int inf=1e18; 8 using namespace std; 9 inline int read(){ 10 int ans=0,f=1;char chr=getchar(); 11 while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();} 12 while(isdigit(chr)) {ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();} 13 return ans*f; 14 }const int M=1e4+5,N=1e3+5; 15 int n,m,s,t,ans; 16 int head[N],nxt[M<<1],ver[M<<1],val[M<<1],tot,f[N][N],g[N][N],a[M],k; 17 inline void add(int x,int y,int z){ver[++tot]=y,nxt[tot]=head[x],val[tot]=z,head[x]=tot;} 18 inline void Clear_All(){ 19 memset(head,0,sizeof(head)); 20 memset(a,0,sizeof(a)); 21 for(int i=1;i<=n;i++)for(int j=0;j<=n;j++)f[i][j]=g[i][j]=inf; 22 tot=f[s][0]=g[s][0]=0;ans=-1; 23 } 24 inline void cmin(int &x,int y){return (void)(x=(x>y)?y:x);} 25 inline void cmax(int &x,int y){return (void)(x=(x>y)?x:y);} 26 inline void DP1(){ 27 for(int i=0;i<n;i++) 28 for(int j=1;j<=n;j++) 29 if(f[j][i]<inf&&a[j]) 30 for(int k=head[j];k;k=nxt[k]) 31 cmin(f[ver[k]][i+1],f[j][i]+val[k]); 32 } 33 inline void DP2(){ 34 for(int i=0;i<n;i++) 35 for(int j=1;j<=n;j++) 36 if(g[j][i]<inf) 37 for(int k=head[j];k;k=nxt[k]) 38 cmin(g[ver[k]][i+1],g[j][i]+val[k]); 39 } 40 inline void Check_Get(){ 41 for(int i=1;i<=n;i++){ 42 if(f[t][i]==inf) continue; 43 int j; 44 for(j=1;j<=i;j++) 45 if(g[t][j]<f[t][i]) break; 46 if(j<=i) continue; 47 for(j=1;j<i;j++) if(g[t][j]!=inf) break; 48 if(j>=i){ 49 ans=inf; break; 50 } 51 int cur=inf; 52 for(j=1;j<i;j++) 53 cur=min(cur,(-f[t][i]+g[t][j])/(i-j)); 54 for(j=i+1;j<=n;j++) 55 if(f[t][i]+i*cur>g[t][j]+j*cur) break; 56 if(j<=n) continue; 57 ans=max(ans,cur); 58 } 59 } 60 signed main(){ 61 // freopen("ernd.in","r",stdin); 62 // freopen("ernd.out","w",stdout); 63 int T=read(); 64 while(T--){ 65 n=read(),m=read(),s=read(),t=read();Clear_All(); 66 for(int i=1,x,y,z;i<=m;i++)x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z); 67 k=read(); 68 for(int i=1,x;i<=k;i++) x=read(),a[x]=1; 69 DP1();DP2(); 70 Check_Get(); 71 if(ans==-1){puts("Impossible");continue;} 72 if(ans==inf){puts("Infinity");continue;} 73 printf("%lld\n",ans); 74 } 75 return 0; 76 }