版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/83478462
正题
最小值最大,想到二分。
二分一个答案k,我们把>=k的边全都删掉,留下的边把图分成了很多个联通块,这些联通块如果都可以连到外面去的话,那么k就是满足条件的(因为走到外面去都要经过>=k的边啊
那么就相当于一张二分图,左边(X集合)是1到n,右边(Y集合)有个节点,分别是个1,个2,...,个n,第a个联通块内的节点连出去的边数都是一样的,(除去自己外其他联通块的xi的和
那么我们现在跑一次二分图最大匹配就可以知道答案了!!
判定二分图的算法除了匈牙利暴力和网络流你还会什么:Hall定理
Hall定理给出了一个解决本问题一个很好的思路:如果X集合内任选a个点,所连的边都覆盖Y集合内>=a个点,那么这张二分图内一定有一个完全匹配。
好,我们来任选。
又要超时。
分情况讨论:
1.选的是不同联通块内的点:连出的边覆盖了Y集合内所有的点,又因为,所以,所以右边的点肯定不少于左边选出来的点。
2.选的是相同联通块内的点,那么是不是要满足对于每一个联通块的大小都不多于其他点的xi总和,。
做完了。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
struct edge{
int x,y,c;
bool operator<(const edge x)const{
return c<x.c;
}
}s[100010];
int w[100010];
long long totw=0;
int f[100010],size[100010],wei[100010];
int findpa(int x){
if(x!=f[x]) return f[x]=findpa(f[x]);
return x;
}
bool check(int x){
for(int i=1;i<=n;i++) f[i]=i,size[i]=1,wei[i]=w[i];
for(int i=1;i<=x;i++){
int fx=findpa(s[i].x),fy=findpa(s[i].y);
if(fx!=fy){
if(size[fx]<size[fy]) swap(fx,fy);
f[fy]=fx;
size[fx]+=size[fy];
wei[fx]+=wei[fy];
}
}
for(int i=1;i<=n;i++){
int fx=findpa(i);
if(size[fx]>totw-wei[fx]) return false;
}
return true;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n-1;i++) scanf("%d %d %d",&s[i].x,&s[i].y,&s[i].c);
for(int i=1;i<=n;i++) scanf("%d",&w[i]),totw+=w[i];
sort(s+1,s+n);
int ans=0;
int l=1,r=n-1;
while(l<=r){
int mid=(l+r)/2;
if(check(mid)){
ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",s[ans+1].c);
}