1. 程式人生 > 實用技巧 >2020 ICPC Asia Taipei-Hsinchu Regional Problem H Optimization for UltraNet (二分,最小生成樹,dsu計數)

2020 ICPC Asia Taipei-Hsinchu Regional Problem H Optimization for UltraNet (二分,最小生成樹,dsu計數)


  • 題意:給你一張圖,要你去邊,使其成為一個邊數為\(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;
}