本题就是大名鼎鼎的最优比率环的问题。

我们不难注意到,平均值就是如下:

x=cicnt\overline{x}=\frac{\sum c_i}{cnt}

其中cntcnt 代表点个数,而cic_{i} 代表权值和。

学过分数规划的想必已经秒了,没事我们一步一步推。

根据题意有:

anscicntx=cix×cnt=cix×cnt0=(cix)0=(xci)0\begin{aligned} ans & \rightarrow \frac{\sum c_i}{cnt} \ge \overline{x} \\ & =\sum c_i \ge \overline{x} \times cnt \\ & =\sum c_{i} - \overline{x} \times cnt \ge 0 \\ & = \sum (c_{i} - \overline{x}) \ge 0 \\ & = \sum (\overline{x}- c_{i}) \le 0 \end{aligned}

这是什么,负环?我们把边权改成midcimid-c_{i} 不就是在让我们找一个环使得权值为负数,这不就是负环吗。

根据上面的不等式,我们不难看出我们需要二分这个x\overline{x}。我们用 SPFA 来判断负环,若有负环我们将x\overline{x} 调小,否则调大,这样就满足题意了。

接下来是代码时间,但是这里需要有几点注意:

  1. BFS 的 SPFA 会炸,请使用 DFS 版本的 SPFA,详情见代码。
  2. double 精度不够,请使用 long double。
  3. 时刻注意精度问题!
  4. 最后结果要输出l+r2\dfrac{l+r}{2},不然会被 hack 精度(没错 long double 还是不够)。

代码通过 uDebug 上所有 hack,同时讨论区内个人 hack 也通过,感谢 hack 提供者们的贡献。也欢迎来 hack 我代码 www。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include<bits/stdc++.h>
#define double long double
using namespace std;
constexpr int MN=55;
constexpr double eps=1e-10;
struct Edge{
int v;
double w;
};
int n,m,tot;
double l,r,dis[MN];
bool vis[MN];
vector<Edge> adj[MN];

int cmp(double x,double y){
if(fabs(x-y)<eps) return 0;
if(x>y) return 1;
return -1;
}

bool dfs(int u,double k){
vis[u]=1;
for(auto e:adj[u]){
int v=e.v;
auto w=e.w;
if(dis[v]>dis[u]+w-k){
dis[v]=dis[u]+w-k;
if(vis[v]||dfs(v,k)){
return vis[u]=0,1;
}
}
}
return vis[u]=0;
}

bool check(double k){
memset(dis,0,sizeof(dis));
for(int i=1;i<=n;i++){
if(dfs(i,k)) return 1;
}
return 0;
}

void init(){
for(int i=1;i<=n;i++) adj[i].clear(),vis[i]=0;
}

void solve(){
init();
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
double w;
cin>>u>>v>>w;
adj[u].push_back({v,w});
}
l=0,r=1e9;
while(r-l>eps){
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
if(cmp(l,1e9)==0) cout<<"No cycle found.\n";
else cout<<fixed<<setprecision(2)<<(l+r)/2<<'\n';
}

int main(){
int T;
cin>>T;
while(T--){
cout<<"Case #"<<++tot<<": ";
solve();
}
return 0;
}