做题记录整理二分5 P3199 [HNOI2009] 最小圈(2022/9/15)

P3199 [HNOI2009] 最小圈

题目看着花里胡哨的,其实本质上就是一个有向图,希望你找一个平均值最小的环

我们选择二分平均值,然后对于每条边都减去这个值(不用真的减),然后是spfa找负环,如果找到负环说明二分大了,再往小的二分就可以了

有一个需要注意的点就是这题的l需要取到-inf,因为答案可能是负数

#include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
#define ll long long
#define mp(a,b) make_pair(a,b)
using namespace std;
int n,m;
int a[500005];
struct node {
	int v,nex;
	double w;
} e[200010];
int hd[500005],cnt;
int vis[500005],kg;
double d[500005];

void ru(int u,int v,double w) {
	e[++cnt].nex=hd[u];
	e[cnt].v=v;
	e[cnt].w=w;
	hd[u]=cnt;
}

void check(int u,double x) {
	vis[u]=1;
	for(int i=hd[u]; i; i=e[i].nex) {
		int v=e[i].v;
		double w=e[i].w;
		if(d[v]>d[u]+w-x) {
			if(vis[v]||kg) {
				kg=1;
				break;
			}
			d[v]=d[u]+w-x;
			check(v,x);
		}
	}
	vis[u]=0;
}

int main() {		
     double w;
	int u,v;
	cin>>n>>m;
	for1(i,1,m) {
		scanf("%d%d",&u,&v);
		scanf("%lf",&w);
		ru(u,v,w);
	}
	double ans;
	double l=-1e8,r=1e8,mid;
	while(r-l>1e-10) {
		mid=(l+r)/2;
		for1(i,1,n) d[i]=0;
		kg=0;
		for1(i,1,n) vis[i]=0;
		for(int i=1; i<=n; ++i) {
			check(i,mid);
			if(kg) break;
		}
		if(kg)r=mid;
		else l=mid,ans=l;
	}
	printf("%.8f",ans);
	return 0;
}
posted @ 2022-09-15 22:05  yyx525jia  阅读(17)  评论(0)    收藏  举报