神圣罗马帝国皇帝

69 篇文章 0 订阅

神圣罗马帝国皇帝


题目描述

中世纪的德意志,战火纷飞。
近来 ZJ JS HN SD 等地区为了扩大自己的版图面积,在内部分别举行了 ZJOI JSOI HNOI 以及 SDOI (或者叫 SDTSC ) 选拔了许多优秀的战士准备发动一场波及到全国各处的战争—— NOI
HN 举行 HNOI 过程中,金天成,绰号光下巴(或者没胡子)红鼻子,二试怒翻盘的成绩强势成为 HN 大军的一员并被封为公爵,率领整个 HN 大军。
HN 这个地方早已不能让金天成满足了!
他在 NOI 之前发动了一场名为 PKUSC 的战役。
金天成凭借着强大的军事才能,卓越的领导能力,强势统一了德意志的大部分地区。
为他成为神圣罗马帝国的皇帝打下了坚实的基础!
现在,金天成需要加强自己统治区域内的运输,他现在有 N 块地盘,每块地盘之间都有些道路,总共有 M 条道路,第 i 条道路有两个属性:运输量 Ci 、危险指数 Di
金天成希望有一种方案,使得维护最少的道路让 N 块地盘两两之间存在通路。
并且选取出来的道路集合 S 的评估指数

Eval=iSCiiSDi

最大!
由于他日理万机,所以这件事情他就让你来做了……


输入格式

第一行两个数字 N M
接下来 M 行,每行三个数字 A,B,C,D ( 1A,BN )。
表示 A B 两块地盘之间有一条运输量为 C ,危险指数为 D 的可用通路。


输出格式

输出一行,表示所有道路选取方案中最大的评估指数。保留 6 位小数。


样例输入

3 3
1 2 2 3 2 3 1 1 1


样例输出

2.000000


样例解释

总共三种选法{边 1,边 2},{边 2,边 3},{边 3,边 1},然后评估值分别为 2 1.5 1.5
最大评估值为 2


数据范围

10% 的数据: 1N5 1M10
30% 的数据: 1N100 1M1000
100% 的数据: 1N104 1M5104 1C104 1D104


Solution

冒险岛
首先二分答案 k ,则

Eval=iSCiiSDik

iS(CikDi)0

所以求最小生成树就可以啦!


Code

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

int n,m,fa[10010],cnt;
const double maxn=1e+8*5; 
struct Edge{int l,r,c,d;}edge[50010];
struct Rdge{int l,r;double c;}rdge[50010];
bool cmp(const Rdge &x,const Rdge &y){return x.c>y.c;} 

int find(int x){
    int tmp=x,pre;
    while(tmp!=fa[tmp])tmp=fa[tmp];
    while(x!=tmp){
        pre=fa[x];fa[x]=tmp;x=pre;
    }
    return tmp;
}

bool judge(double x){
    double sub=0;
    for(int i=1;i<=m;i++){
        rdge[i].l=edge[i].l; rdge[i].r=edge[i].r;
        rdge[i].c=edge[i].c-edge[i].d*x;
    }
    for(int i=1;i<=n;i++)fa[i]=i;
    sort(rdge+1,rdge+m+1,cmp); cnt=0;
    for(int i=1;i<=m&&cnt<n-1;i++){
        int fl=find(rdge[i].l),fr=find(rdge[i].r);
        if(fl!=fr){cnt++;fa[fl]=fr;sub+=rdge[i].c;}
    }
    return sub>=0;
}

double binary(double l,double r){
    while(r-l>=1e-8){
        double mid=(l+r)/2;
        if(judge(mid))l=mid;
        else r=mid;
    }
    return l;
}

int main(){

    freopen("emperor.in","r",stdin);
    freopen("emperor.out","w",stdout);

    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&edge[i].l,&edge[i].r,&edge[i].c,&edge[i].d);
    }

    printf("%.6lf\n",binary(0,maxn));

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值