1 条题解

  • 0
    @ 2025-8-24 22:05:35

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 灯芯糕
    草,赶紧先找个坑把自己埋了

    搬运于2025-08-24 22:05:35,当前版本为作者最后更新于2019-02-13 15:45:14,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    solution:solution:

    这题其实牵扯到了一种普遍的二分答案的方法,与[HNOI2009 最小圈](https://www.luogu.org/problemnew/show/P3199) 一样有许多的共同点。因为这些题要求我们输出的ans都是针对权值总和不断计算而来的。本题就是个经典例题(二分答案求最优比率生成树),经典因为我们可以直接通过题意看出它要求我们输出的**最终答案**实际上就是:
    

    $ans= \frac{F-\sum{v_i} \quad (i\in S)}{\sum{t_i}\quad (i\in S)} $

    (其中S为我们最终所选择的最优生成树的边权的集合)我们将这个式子转换一下,可以得到:

    F=ansti+viF=ans*\sum{t_i}+\sum{v_i}

    F=ansti+vi(iS)F=\sum{ans*t_i+v_i}\quad _{(i\in S)}

    注:这一点我们一定要清楚:此时等式右边就是生成树(只不过;树边权变成了ansti+vians*t_i+v_i

    ​ 上述式子中我们的ans就是我们最终要输出的最优比率,这个ans是我们可以二分得到的!因为答案具有单调性,当然这个需要我们进一步证明:首先,因为ans是最优比率,那么我们可以知道,对于图上的任意一个生成树的边的集合K,一定有:$\frac{F-\sum{v_i} \quad (i\in K)}{\sum{t_i}\quad (i\in K)}\leq ans $ (根据上述方式转化得:Fansti+vi(iK)F\leq \sum{ans*t_i+v_i}\quad {(i\in K)})只有当我们的集合K取到最优的生成树时也就是K=S的时候,这个式子才会是等式!然后我们来证明二分的正确性:当我们二分求ans的时候,对于我们当前的比率x,一定有这三种情况:

    1.1. x>ansx>ans

    当我们比率为ans时:Fansti+vi(iK)F\leq \sum{ans*t_i+v_i}\quad {(i\in K )}

    现在我们的比率x比ans还大!那我们的肯定有:F<xti+vi(iK)F< \sum{x*t_i+v_i}\quad {(i\in K)}

    2.2. x=ansx=ans

    我已经说过了:Fansti+vi(iK)F\leq\sum{ans*t_i+v_i}\quad {(i\in K)}

    3.3. x<ansx<ans

    这个时候我们发现我们不能确认FFansti+vi(iK)\sum{ans*t_i+v_i}\quad {(i\in K)} 的大小关系了!因为我们的K是任意生成树的边的集合,此时我们小于等于大于都有可能,但也正是如此,我们可以证明一定存在一个生成树边的集合满足F>xti+vi(iS)F>\sum{x*t_i+v_i}\quad {(i\in S)}因为就拿我们ans情况下的最优边集S,因为 F=ansti+vi(iS)F=\sum{ans*t_i+v_i}\quad {(i\in S)} 此时我们的x<ansx<ans 所以F>xti+vi(iS)F>\sum{x*t_i+v_i}\quad {(i\in S)}

    如何二分(为什么用最小生成树):

    那我们证明了上面这个东西有什么用呢?根据上述三个关系的特性(就是那三个不等式),我们发现当我们将x带进去后,只要求得最小的 ansti+vi(iK)\sum{ans*t_i+v_i}\quad {(i\in K)} 我们就可以直接判断出x和ans的关系了!

    我们将最小的ansti+vi(iK)\sum{ans*t_i+v_i}\quad {(i\in K)} 看做min

    1. F<minF<min,则必有F<xti+vi(iK)F< \sum{x*t_i+v_i}\quad {(i\in K)} 这不就是第一种情况吗?x必然大于ans
    2. F=minF=min,则必有Fxti+vi(iK)F \leq \sum{x*t_i+v_i}\quad {(i\in K)} 这不就是第二种情况吗?x必然等于ans
    3. F>minF>min,则x必然不大于也不等于ans,那不就是小于ans吗?(上面证过了若x<ans,则必存在一个KF>xti+vi(iK)F>\sum{x*t_i+v_i}\quad {(i\in K)}

    那我们如何求最小的xti+vi(iK)\sum{x*t_i+v_i}\quad {(i\in K)} 呢? 这就是求最小生成树嘛(只不过;树边权变成了ansti+vians*t_i+v_i,我们每一次都重新排个序就行了)!

    总复杂度:O(mlogmlog(rl+1))O(m *logm*log(r-l+1))

    code:code:

    //耗时 41ms 内存 1036KB
    #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 rg register int
    
    using namespace std;
    
    const db cha=1e-9;
    
    struct su{
    	int x,y,v,t;db k;
    }a[10005];
    
    int n,m,f;
    int s[405];
    
    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 bool cmp(su x,su y){return x.k<y.k;}
    
    inline int get(int x){
    	if(s[x]==x)return x;
    	return s[x]=get(s[x]);
    }
    
    inline bool check(db x){
    	db res=0;
    	for(rg i=1;i<=n;++i)s[i]=i;//并查集
    	for(rg i=1;i<=m;++i)
    		a[i].k=x*a[i].t+(db)a[i].v;//更新边权
    	sort(a+1,a+m+1,cmp);//kruskal求最小生成树
    	for(rg i=1;i<=m;++i)
    		if(get(a[i].x)!=get(a[i].y))
    			res+=a[i].k,s[get(a[i].x)]=get(a[i].y);//求新边权的总和
    	return f>res?0:1;
    }
    
    int main(){
    	freopen("quake.in","r",stdin);
    	freopen("quake.out","w",stdout);
    	n=qr(),m=qr(),f=qr();
    	for(rg i=1;i<=m;++i)
    		a[i]=su{qr(),qr(),qr(),qr()};
    	if(check(0))puts("0.0000"),exit(0);//特判
    	db l=0,r=1e14,mid;
    	while(r-l>cha){//二分答案
    		mid=(l+r)/2;
    		if(check(mid))r=mid-cha;
    		else l=mid+cha;
    	}printf("%.4lf",l);
    	return 0;
    }
    
    
    • 1

    信息

    ID
    3985
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者