Tree,noi.ac模擬賽,Hall定理
阿新 • • 發佈:2018-11-02
正題
最小值最大,想到二分。
二分一個答案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); }