HDU - 4424 Conquer a New Region 並查集好題

題意:給定N個點的樹。定義d(u,v)爲路徑u到v的容量,其值爲路徑上最小的邊權。求一個點作爲root,使得其他所有點到該點的容量和最大,求最大值。

思路:考慮邊權最小的邊e,則root一定在其左子樹或者右子樹上,假設在左子樹上,那麼e的貢獻就是

w[e] * sz[rchild] + ans[lchild],然後將最小邊去掉,樹就被分成了兩部分,每一部分都會有一個最小邊,這樣分治下去就能求出答案,但是這樣做有一個難點就是無法快速求得最小邊,因此我們逆向思維,將所有邊按邊權從大到小排序後不斷合併子樹,合併過程可以用並查集維護,這樣就能很容易的求出答案了。

代碼:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
const int MAXN = 200010;
struct edge{
	int u, v, w;
	bool operator < (edge a) const
	{
		return w > a.w;
	}
}e[MAXN];
int f[MAXN], sz[MAXN];
ll we[MAXN];
int getf(int k)
{
	return k == f[k] ? k : f[k] = getf(f[k]);
} 
int main()
{
	int n, u, v, w;
	while(~scanf("%d", &n))
	{
		for(int i = 1; i < n; i++)
		{
			scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
			f[i] = i;
			sz[i] = 1;
			we[i] = 0;
		}
		f[n] = n; sz[n] = 1; we[n] = 0;
		sort(e + 1, e + n);
		for(int i = 1; i < n; i++)
		{
			w = e[i].w;
			u = getf(e[i].u); v = getf(e[i].v);
			if(u == v) continue;
			if(1ll * sz[v] * w + we[u] < 1ll * sz[u] * w + we[v])
			{
				f[u] = v; sz[v] += sz[u]; we[v] += 1ll * sz[u] * w;
			}
			else
			{
				f[v] = u; sz[u] += sz[v]; we[u] += 1ll * sz[v] * w;
			}
		}
		cout << *max_element(we + 1, we + n + 1) << endl; 
	}
 	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章