2020 ICPC Asia Taipei-Hsinchu Regional Problem H Optimization for UltraNet (二分,最小生成樹,dsu計數)
阿新 • • 發佈:2020-12-26
- 題意:給你一張圖,要你去邊,使其成為一個邊數為\(n-1\)的樹,同時要求樹的最小邊權最大,如果最小邊權最大的情況有多種,那麼要求總邊權最小.求生成樹後的所有簡單路徑上的最小邊權和.
- 題解:剛開始想寫最大生成樹的,但是很明顯不能滿足總邊權最小的要求.所以這裡我們可以用二分,二分最小邊權的值,然後再去跑kruskal看是否能構造成一顆樹,這樣的話我們就能得出滿足題目條件的樹.之後我們再將邊權從大到小排序來列舉,用並查集維護連通塊,假如兩個塊不連通,因為此時的邊權是目前最小的,簡單路徑數是兩個連通塊數量的乘積,很顯然貢獻為包含這條邊的簡單路徑數*邊權.
- 程式碼:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,m; int p[N],sz[N]; struct misaka{ int a,b; int val; bool operator < (const misaka & mikoto) const{ return val<mikoto.val; } }e[N]; vector<misaka> v; int find(int x){ if(p[x]!=x) p[x]=find(p[x]); return p[x]; } bool check(int x){ if(m-x+1<n-1) return false; rep(i,1,n) p[i]=i; int cnt=0; rep(i,x,m){ int fa=find(e[i].a); int fb=find(e[i].b); if(fa!=fb){ p[fa]=fb; cnt++; } } return cnt==n-1; } void build(int x){ int cnt=0; rep(i,1,n) p[i]=i; rep(i,x,m){ int a=e[i].a; int b=e[i].b; int val=e[i].val; int fa=find(a); int fb=find(b); if(fa==fb) continue; p[fa]=fb; v.pb({a,b,val}); //cout<<a<<' '<<b<<' '<<val<<'\n'; cnt++; if(cnt==n-1) break; } } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>m; rep(i,1,n){ p[i]=i; sz[i]=1; } rep(i,1,m){ cin>>e[i].a>>e[i].b; cin>>e[i].val; } sort(e+1,e+1+m); int l=1,r=m; while(l<r){ int mid=(l+r+1)>>1; if(check(mid)) l=mid; else r=mid-1; } build(l); reverse(v.begin(),v.end()); ll ans=0; int cnt=1; rep(i,1,n) p[i]=i; for(auto w:v){ int fa=find(w.a); int fb=find(w.b); p[fa]=fb; ans+=1ll*sz[fa]*sz[fb]*w.val; sz[fb]+=sz[fa]; } cout<<ans<<'\n'; return 0; }