• [HNOI2009]最小圈 (二分答案+负环)


    题面:[HNOI2009]最小圈

    题目描述:

    考虑带权的有向图 $ G=(V,E) $ 以及 $ w:E ightarrow R $ ,每条边 $ e=(i,j)(i eq j,iin V,jin V) $ 的权值定义为 $ w_{i,j} $ ,令 $ n=|V| $ 。 $ c=(c_1,c_2,cdots,c_k)(c_iin V) $ 是 $ G $ 中的一个圈当且仅当 $ (c_i,c_{i+1})(1le ilt k) $ 和 $ (c_k,c_1) $ 都在 $ E $ 中,这时称 $ k $ 为圈 $ c $ 的长度同时令 $ c_{k+1}=c_1 $ ,并定义圈 $ c=(c_1,c_2,cdots,c_k) $ 的平均值为 $ mu(c)=sumlimits_{i=1}^{k} w_{c_i,c_{i+1}}/k $ ,即 $ c $ 上所有边的权值的平均值。令 $ mu'(c)=Min(mu(c)) $ 为 $ G $ 中所有圈 $ c $ 的平均值的最小值。现在的目标是:在给定了一个图 $ G=(V,E) $ 以及 $ w:E ightarrow R $ 之后,请求出 $ G $ 中所有圈 $ c $ 的平均值的最小值 $ mu'(c)=Min(mu(c)) $

    输入格式:

    第一行2个正整数,分别为 $ n $ 和 $ m $ ,并用一个空格隔开,只用 $ n=|V|,m=|E| $ 分别表示图中有 $ n $ 个点 $ m $ 条边。
    接下来m行,每行3个数 $ i,j,w_{i,j} $ ,表示有一条边 $ (i,j) $ 且该边的权值为 $ w_{i,j} $ 。输入数据保证图 $ G=(V,E) $ 连通,存在圈且有一个点能到达其他所有点。

    输出格式:

    请输出一个实数 $ mu'(c)=Min(mu(c)) $ ,要求输出到小数点后8位。

    输入样例#1:

    4 5
    1 2 5
    2 3 5
    3 1 5
    2 4 3
    4 1 3

    输出样例#1:

    3.66666667

    输入样例#2:

    2 2
    1 2 -2.9
    2 1 -3.1

    输出样例#2:

    -3.00000000

    说明:

    对于100%的数据, $ nle 3000,mle 10000,|w_{i,j}| le 10^7 $



    $ solution: $

    这道题要我们求平均值的最小值,所以我们考虑二分答案的可能性,先列出答案的意义:

    $ ans=frac{sumlimits_{i=1}^{k} w_{c_i,c_{i+1}}}{K}quad _{(c_{k+1}=c_1)} $

    我们将它转换一下:

    $ ans imes k={sumlimits_{i=1}^{k} w_{c_i,c_{i+1}}}quad _{(c_{k+1}=c_1)} $

    $ 0=sumlimits_{i=1}^{k} (w_{c_i,c_{i+1}})-ans imes kquad _{(c_{k+1}=c_1)} $

    $ 0=sumlimits_{i=1}^{k}(w_{c_i,c_{i+1}}-ans)quad _{(c_{k+1}=c_1)} $

    这样我们发现它已经化成了一个二分答案的常用等式(等式右边可以 $ O(n) $ 求出来,且具备单调性)而我们注意到等式左边为0,所以我们可以二分ans,并将边权改为 $ w_{c_i,c_{i+1}}-mid $ ,然后求负环即可。

    为什么可以这样做呢?这个较地震那一题好讲一些,我们当前二分出来的平均值mid,我们将每一条边的边权都减去它,如果存在负环,说明这个环上所有边权实际边权值加起来的平均值一定小于mid!(这里需要仔细想一下)



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    
    #define ll long long
    #define db double
    #define inf 0x7fffffff
    #define rg register int
    
    using namespace std;
    
    const db cha=1e-9;
    
    struct su{
    	db v;int to,next;
    }a[10005];
    
    bool f;
    int n,m,top;
    int tou[3005];
    bool vis[3005];
    db mid,dis[3005];
    
    inline int qr(){
    	char ch;
    	while((ch=getchar())<'0'||ch>'9');
    	int res=ch^48;
    	while((ch=getchar())>='0'&&ch<='9')
    		res=res*10+(ch^48);
    	return res;
    }
    
    inline void add(int x,int y){
    	scanf("%lf",&a[++top].v);
    	a[top].to=y;
    	a[top].next=tou[x];
    	tou[x]=top;
    }
    
    inline void spfa(int i){
    	vis[i]=1;
    	for(rg j=tou[i];j;j=a[j].next){
    		if(dis[a[j].to]>dis[i]+a[j].v-mid){
    			dis[a[j].to]=dis[i]+a[j].v-mid;
    			if(vis[a[j].to])return void(f=1);
    			else spfa(a[j].to);
    		}
    	}vis[i]=0;
    }
    
    inline bool check(){
    	for(rg i=1;i<=n;++i)
    		dis[i]=vis[i]=0;;f=0;
    	for(rg i=1;i<=n&&!f;++i)
    		if(!vis[i])spfa(i);
    	return f;
    }
    
    int main(){
    	freopen("cycle.in","r",stdin);
    	freopen("cycle.out","w",stdout);
    	n=qr(),m=qr();
    	for(rg i=1;i<=m;++i)
    		add(qr(),qr());
    	db l=-1e7,r=1e7;
    	while(l<=r){
    		mid=(l+r)/2;
    		if(check())r=mid-cha;
    		else l=mid+cha;
    	}printf("%.8lf
    ",l);
    	return 0;
    }
    
    
  • 相关阅读:
    koa2 中间件里面的next到底是什么
    nodejs 之 nvm和pm2
    javascript瀑布流
    转使用chrome命令行:disable
    MariaDB 用户与授权
    MariaDB TRANSACTION
    IHS .kdb转crt,key
    MariaDB TRIGGER
    MariaDB CURSOR
    MariaDB PROCEDURE
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/10367943.html
Copyright © 2020-2023  润新知