link
题目大意:
给出一个 N N N个点 M M M条边的有向图,求边权平均值最小的连通块。
题目分析:
首先,每个连通块的边权平均值为: x = ∑ k = i j a k j − i + 1 x=\frac{\sum\limits_{k=i}^{j}a_{k}}{j-i+1} x=j−i+1k=i∑jak ( i i i到 j j j联通, a i a_{i} ai表示边权)。题目要求平均值最小,即, a n s > = ∑ k = i j a k j − i + 1 ans>=\frac{\sum\limits_{k=i}^{j}a_{k}}{j-i+1} ans>=j−i+1k=i∑jak( i i i到 j j j联通) 。同时, a n s ans ans是具有单调性的,所以我们考虑二分答案。
我们二分出一个 a n s ans ans。考虑对原式子进行化简:
a n s > = ∑ k = i j a k j − i + 1 ans>=\frac{\sum\limits_{k=i}^{j}a_{k}}{j-i+1} ans>=j−i+1k=i∑jak( i i i到 j j j联通)
a n s ∗ ( j − i + 1 ) > = ∑ k = i j a k ans*(j-i+1)>=\sum\limits_{k=i}^{j}a_{k} ans∗(j−i+1)>=k=i∑jak
a n s ∗ ( j − i + 1 ) > = a i + a i + 1 + . . . + a j − 1 + a j ans*(j-i+1)>=a_{i}+a_{i+1}+...+a_{j-1}+a{j} ans∗(j−i+1)>=ai+ai+1+...+aj−1+aj
( a i − a n s ) + ( a i + 1 − a n s ) + . . . + ( a j − 1 − a n s ) + ( a j − a n s ) < = 0 (a_{i}-ans)+(a_{i+1}-ans)+...+(a_{j-1}-ans)+(a_{j}-ans)<=0 (ai−ans)+(ai+1−ans)+...+(aj−1−ans)+(aj−ans)<=0 ①
即,当我们二分出的一个答案 a n s ans ans在①式成立的情况下是合法的。
现在我们考虑如何维护①式。
观察可知,我么可以将原来的边权 a i a_{i} ai转化为 a i − a n s a_{i}-ans ai−ans,然后判断在这个新图里面是否有负环,如果有,那么①式肯定成立。即我们二分出来的这个答案 a n s ans ans合法。
注意事项:
1.用 S P F A SPFA SPFA判断是否存在负环是通过记录每个点入队的次数来实现的。如果一个点被超过 n n n个点更新,则一定存在负环。
2.这个图有可能不连通。(比如存在 1 − > 2 , 3 − > 2 1->2,3->2 1−>2,3−>2这种情况)所以在 S P F A SPFA SPFA之前,需要将所有的点加入队列。
3. S P F A SPFA SPFA可以用双端队列或者优先队列优化。
4.由于我们二分的是一个实数,所以需要 e p s eps eps来确定精度。
5.由于是多组数据,所以要注意初始化。
6.如果我们 a n s = M a x e d g e + 1 ans=Maxedge+1 ans=Maxedge+1仍没有负环,则说明该图没有环。即输出"No cycle found."
代码如下:
#include <bits/stdc++.h>
#define inf 1e8+7.0
#define eps 1e-8
using namespace std;
const int maxn=1e4+5;
double dis[maxn];
int used[maxn],head[maxn],num[maxn];
double l,r;
int size=0,n,m,cas,t;
struct EDGE
{
int u,v,nxt;
double w;
}edge[maxn];
void add(int u,int v,double w)
{
edge[size].u=u;
edge[size].v=v;
edge[size].w=w;
edge[size].nxt=head[u];
head[u]=size++;
}
bool SPFA()
{
deque<int>q;
for(int i=1;i<=n;i++)
{
used[i]=0;
num[i]=0;
}
for(int i=1;i<=n;i++)
q.push_back(i),dis[i]=0;
while(!q.empty())
{
int u=q.front();
q.pop_front();
used[u]=0;
for(int i=head[u];~i;i=edge[i].nxt)
{
int v=edge[i].v;
if(dis[v]-(dis[u]+edge[i].w)>eps)
{
dis[v]=dis[u]+edge[i].w;
if(used[v])continue;
used[v]=0;
if(!q.empty())
{
if(dis[q.front()]-dis[v]>eps)q.push_front(v);
else q.push_back(v);
}
else q.push_back(v);
num[v]++;
if(num[v]>n)return true;
}
}
}
return false;
}
bool check(double mid)
{
for(int i=0;i<size;i++)
{
edge[i].w-=mid;
}
bool flag=SPFA();
for(int i=0;i<size;i++)
{
edge[i].w+=mid;
}
return flag;
}
void init()
{
freopen("in.in","r",stdin);
}
void readdata()
{
memset(head,-1,sizeof(head));
memset(edge,0,sizeof(edge));
size=0;
l=inf,r=-inf;
scanf("%d%d",&n,&m);
int u,v;
double w;
for(int i=1;i<=m;i++)
{
scanf("%d%d%lf",&u,&v,&w);
add(u,v,w);
r=max(r,w);
l=min(l,w);
}
printf("Case #%d: ",++cas);
}
void work()
{
if(!check(r+1))
{
printf("No cycle found.\n");
return;
}
while(r-l>=eps)
{
double mid=l+(r-l)/2;
if(check(mid))
{
r=mid;
}
else
{
l=mid;
}
}
printf("%.2lf\n",r);
}
int main()
{
init();
scanf("%d",&t);
while(t--)
{
readdata();
work();
}
return 0;
}