爆弹虐场
题目描述
某年某月某日,Kiana 结识了一名爆弹虐场的少年。
Kiana 仗着自己多学了几年OI,所以还可以勉勉强强给这位少年 讲一些自己擅长的题。具体来说,Kiana 先给这位少年灌输了n 个毫不相干的知识点,然后再通过自己的[数据删除]技术把这些知识点强行联系在一起。
由于这位少年有着爆弹虐场的实力,所以对于每个Kiana 准备强行构造的联系,他都能够自己想出来,不过会花费更多的时间。具体来说,Kiana 一共有m 个联系,每个联系可以把两个不相干的知识点连在一起,如果由Kiana 直接来讲第i 个联系,需要花费ti 的时间, 而如果由少年自己想出来,则需要花费Ti 的时间。
为了偷懒,Kiana 只需要自己讲的或少年想出来的联系能刚好把知识点全部直接或间接串在一起就可以了。但为了保证教学质量, Kiana 觉得至少有k 个联系需要少年自己想出来。由于Kiana 耐心有限,她希望无论是自己讲或是少年自己想,构造的联系中花费时间最长的一个用时最短。
现在Kiana 想知道,满足这些条件的情况下,构造的联系中耗时最长的一个的最短用时是多少。由于她不会算,所以希望由你告诉她。
输入格式
输入文件包括m+1 行。
第一行包含三个正整数n,k 和m,分别表示知识点的数量,Kiana 希望少年自己想出来的联系的数量和联系的总数量。
接下来m 行,每行包含四个正整数a,b,Ti 和ti,表示在知识点a 和b 之间可以构造出一个联系,这个联系由少年自己想出来需要花费 Ti 的时间,而Kiana 直接讲出来需要花费ti 的时间。
输出格式
输出文件包括一行。
第一行包含一个正整数,表示构造的联系中耗时最长的一个的最短用时。
样例 #1
样例输入 #1
4 2 5
1 2 6 5
1 3 3 1
2 3 9 4
2 4 6 1
3 4 4 2
样例输出 #1
4
提示
对于30%的数据,1<=n<=10,n-1<=m<=15,
对于60%的数据,1<=n<=500,n-1<=m<=1000,
对于100%的数据,1<=k<n<=10000,n-1<=m<=20000,
1<=ti<Ti<=10^6。
数据保证一定存在可行解。
思路
本道题首先先提出了最大值最小,那么就用二分。又因为它是求树上的和最小,那么可以想到最小生成树,最小生成树最好的板子就是kruscal。
- 细节1:我们得注意我们是尽量要让自己做,当自己做的时间相同的话,我们再选ti的时间。
- 也就是:
代码
//最小生成树
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 50010,M = 2*N;
int n,m,k;
struct E{
int x,y,T,t;
}e[M];
int p[N];
int find(int x){
if(x!=p[x])p[x]=find(p[x]);
return p[x];
}
bool cmp(E a,E b){
if(a.T==b.T)return a.t<b.t;
return a.T<b.T;
}
bool check(int x){
int k1=0,tot=0;
for(int i=1;i<=m;i++){
int a=e[i].x,b=e[i].y,w1=e[i].T,w2=e[i].t;
int pa=find(a),pb=find(b);
if(x<min(w1,w2))continue;
if(pa!=pb){
p[pa]=pb;
if(w1<=x){
k1++;//连上Ti
}
tot++;
}
if(tot==n-1)break;//全联通了
}
if(k1<k)return false;
if(tot<n-1)return false;
return true;
}
int main(){
cin>>n>>k>>m;
for(int i=1;i<=m;i++){
int a,b,c,d;
cin>>a>>b>>c>>d;
e[i]={a,b,c,d};
}
sort(e+1,e+1+m,cmp);
int l=0,r=2e6+1;
while(l+1!=r){
for(int i=1;i<=n;i++)p[i]=i;
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid;
}
cout<<r;
return 0;
}