D120 最短路→差分约束 SPFA 算法 P4926 [1007] 倍杀测量者
D120 最短路→差分约束 SPFA 算法 P4926 [1007] 倍杀测量者_哔哩哔哩_bilibili
若存在至少满足一个约束条件的解,输出最大的 T. 若不存在,输出 -1.
思路
注意本题与前面题的不同:前面题都是求 “满足所有约束条件的解”,本题是求 “存在至少满足一个约束条件的解”
设 $f[A]$ 表示选手 $A$ 得的分数
A 说 “我没 k 倍杀选手 B 就吃顿好的” ,变成约束条件:$f[A]\le f[B]*k$
A 说 “选手 B 把我 k 倍杀我就吃顿好的” ,变成约束条件:$f[A]*k\le f[B]$,即 $f[A]\le f[B]*\frac{1}{k}$
设定一个正常数 T 后,约束条件变为:
1. $f[A]\le f[B]*(k-T)$
2. $f[A]\le f[B]*\frac{1}{k+T}$
对于已知选手的得分 $f[A]=x$,变成约束条件:$f[A]\le x$ 且 $f[A]\ge x$
令 0 号点的得分为 $f[0]=1$,则 $f[A]\le f[0]*x$ 且 $f[A]\ge f[0]*x$
3. $f[A]\le f[0]*x$
4. $f[0]\le f[A]*\frac{1}{x}$
把变量 $f[i]$ 看做点,下标做为点的编号,连边建图,跑乘积最长路,如果存在环,则说明满足了一个约束条件
我们发现,$T$ 越小,$(k-T)$ 与 $\frac{1}{k+T}$ 越大,条件越容易成立,所以具有单调性,可以二分 $T$

相关板子:
D116【模板】最短路→差分约束 SPFA 算法 P5960 差分约束 - 董晓 - 博客园
// 差分约束 SPFA 算法 O(NM*log(10^7)) #include<bits/stdc++.h> using namespace std; const int N=1010,M=N*3; int h[N],idx,ne[M],to[M]; double ww[M]; void add(int x,int y,double w){ to[++idx]=y;ww[idx]=w;ne[idx]=h[x];h[x]=idx; } int n,s,t,x[N],cnt[N],vis[N]; struct F{int o,A,B,k;}f[N]; double d[N]; void build(double T){ idx=0; memset(h,0,sizeof(h)); for(int i=1;i<=s;i++){ auto [o,A,B,k]=f[i]; if(o==1) add(B,A,k-T); else add(B,A,1.0/(k+T)); } for(int i=1;i<=n;i++) if(x[i]){ add(0,i,x[i]); add(i,0,1.0/x[i]); } } int spfa(double T){ build(T); //建图 queue<int> q; for(int i=0;i<=n;i++) d[i]=1,cnt[i]=0,q.push(i),vis[i]=1; while(!q.empty()){ int u=q.front(); q.pop();vis[u]=0; for(int i=h[u];i;i=ne[i]){ int v=to[i]; double w=ww[i]; if(d[v]<d[u]*w){ d[v]=d[u]*w; //乘积最长路 cnt[v]=cnt[u]+1; if(cnt[v]>=n+1) return true; //有环 if(!vis[v]) q.push(v),vis[v]=1; } } } return false; //无环 } int main(){ cin>>n>>s>>t; for(int i=1,o,A,B,k;i<=s;i++){ cin>>o>>A>>B>>k; f[i]={o,A,B,k}; } for(int i=1,C,x1;i<=t;i++) cin>>C>>x1,x[C]=x1; double l=0,r=10,mid; while(r-l>1e-6){ mid=(l+r)/2; spfa(mid)?l=mid:r=mid; } if(l==0) puts("-1"); //找不到正T,无解 else printf("%.6lf\n",l); }
浙公网安备 33010602011771号