1. 程式人生 > 實用技巧 >[HNOI2014]米特運輸

[HNOI2014]米特運輸

題目描述

米特是D星球上一種非常神祕的物質,蘊含著巨大的能量。在以米特為主要能源的D星上,這種米特能源的運輸和儲存一直是一個大問題。

D星上有N個城市,我們將其順序編號為1到N,1號城市為首都。這N個城市由N-1條單向高速通道連線起來,構成一棵以1號城市(首部)為根的樹,高速通道的方向由樹中的兒子指向父親。樹按深度分層:根結點深度為0,屬於第1層;根結點的子節點深度為1,屬於第2層;依此類推,深度為i的結點屬於第i+l層。

建好高速通道之後,D星人開始考慮如何具體地儲存和傳輸米特資源。由於發展程度不同,每個城市儲存米特的能力不盡相同,其中第i個城市建有一個容量為A[i]的米特儲存器。這個米特儲存器除了具有儲存的功能,還具有自動收集米特的能力。

如果到了晚上六點,有某個儲存器處於未滿的狀態,它就會自動收集大氣中蘊含的米特能源,在早上六點之前就能收集滿;但是,只有在儲存器完全空的狀態下啟動自動收集程式才是安全的,未滿而又非空時啟動可能有安全隱患。

早上六點到七點間,根節點城市(1號城市)會將其儲存器裡的米特消耗殆盡。根節點不會自動蒐集米特,它只接受子節點傳輸來的米特。

早上七點,城市之間啟動米特傳輸過程,傳輸過程逐層遞進:先是第2層節點城市向第1層(根節點城市,即1號城市)傳輸,直到第1層的儲存器滿或第2層的儲存器全為空;然後是第3層向第2層傳輸,直到對於第2層的每個節點,其儲存器滿或其予節點(位於第3層)的儲存器全為空;依此類推,直到最後一層傳輸完成。傳輸過程一定會在晚上六點前完成。

由於技術原因,運輸方案需要滿足以下條件:

不能讓某個儲存器到了晚上六點傳輸結束時還處於非空但又未滿的狀態,這個時候儲存器仍然會啟動自動收集米特的程式,而給已經儲存有米特的儲存器啟動收集程式可能導致危險,也就是說要讓儲存器到了晚上六點時要麼空要麼滿;

關於首都——即1號城市的特殊情況, 每天早上六點到七點間1號城市中的米特儲存器裡的米特會自動被消耗殆盡,即運輸方案不需要考慮首都的米特怎麼運走;

除了1號城市,每個節點必須在其子節點城市向它運輸米特之前將這座城市的米特儲存器中原本存有的米特全部運出去給父節點,不允許儲存器中殘存的米特與外來的米特發生混合;

運向某一個城市的若干個來源的米特數量必須完全相同,不然,這些來源不同的米特按不同比例混合之後可能發生危險。

現在D星人已經建立好高速通道,每個城市也有了一定儲存容量的米特儲存器。為了滿足上面的限制條件,可能需要重建一些城市中的米特儲存器。你可以,也只能,將某一座城市(包括首都)中原來存在的米特儲存器摧毀,再新建一座任意容量的新的米特儲存器,其容量可以是小數(在輸入資料中,儲存器原始容量是正整數,但重建後可以是小數),不能是負數或零,使得需要被重建的米特儲存器的數目儘量少。


認真讀完題後發現這道題的限制是非常強的,確認了一個點的值後整個樹的值也就確認了,那麼如果確認了一個值的話就可以得到當前的答案。這樣顯然保留一個節點的值不會是最不好的(肯定不可能所有點的值都變化),那麼就有一個暴力的做法,列舉每個點,考慮保留這個點不變的答案。於是我們發現如果保留了某個點那其它點也有可能不變,把這樣的點放在同一個集合中,會有很多個集合,現在我們需要快速計算出這些集合的大小的最大值。整棵樹中根是特殊的,對於每個點,如果保留其,那麼根的值也可以計算出來,而同一個集合中的點對應的根的值應該是一樣的,這樣就可以快速計算了。注意直接計算會炸longlong,我們又發現 \(ln{ab}=ln{a}+ln{b}\),所以可以通過取對數把乘法改為加法。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps = 1e-10;
const ll mod = 998244353;
template <typename T> void read(T &x) {
	T f = 1;
	char ch = getchar();
	for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
	for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
struct node{
	int pre, to;
}edge[500005 << 1];
int head[500005], tot;
int n;
double a[500005];
double deg[500005];
double dp[500005];
int ans, MIN;
int cur;
map<int, int> mp;
int fa[500005];
double val[500005];
void add_edge(int u, int v) {
	edge[++tot] = node{head[u], v};
	head[u] = tot;
}
void dfs(int x) {
	if (x > 1 && deg[x]) val[x] = val[fa[x]] + log(deg[x]);
	for (int i = head[x]; i; i = edge[i].pre) {
		int y = edge[i].to;
		if (y == fa[x]) continue;
		dfs(y);
	}
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1; i < n; i++) {
		int u, v;
		read(u); read(v);
		add_edge(u, v);
		add_edge(v, u);
		deg[u]++;
		fa[v] = u;
	}
	val[1] = log(deg[1]);
	dfs(1);
	dp[1] = log(a[1]);
	for (int i = 2; i <= n; i++) {
		dp[i] = val[fa[i]] + log(a[i]);
	}
	sort(dp + 1, dp + 1 + n);
	ans = 1;
	for (int i = 2; i <= n; i++) {
		if (fabs(dp[i] - dp[i - 1]) <= eps) {
			ans++;
		} else {
			MIN = max(MIN, ans);
			ans = 1;
		}
	}
        MIN = max(MIN, ans);   
	printf("%d\n", n - MIN);
	return 0;
}