1. 程式人生 > >P3237 [HNOI2014]米特運輸【題解】

P3237 [HNOI2014]米特運輸【題解】

題目描述

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

D

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

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

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

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

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

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

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

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

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

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

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

輸入輸出格式

輸入格式:

第一行是一個正整數 N N ,表示城市的數目。
接下來 N N 行,每行一個正整數,其中的第i行表示第i個城市原來存在的米特儲存器的容量。
再接下來是 N 1 N-1 行,每行兩個正整數 a a b b 表示城市 b b 到城市 a a 有一條高速通道 ( a b ) (a≠b)

輸出格式:

輸出檔案僅包含一行,一個整數,表示最少的被重建(即修改儲存器容量)的米特儲存器的數目。

輸入輸出樣例
輸入樣例#1:

5
5
4
3
2
1
1 2
1 3
2 4
2 5

輸出樣例#1:

3

說明

【樣例解釋】

一個最優解是將 A [ 1 ] A[1] 改成 8 8 A [ 3 ] A[3] 改成 4 4 A [ 5 ] A[5] 改成 2 2 。這樣, 2 2 3 3 運給 1 1 4 4 量相等, 4 4 5 5 運給 2 2 的量相等,且每天晚上六點的時候, 1 1 2 2 滿, 3 3 4 4 5 5 空,滿足所有限制條件。

對於 100 100 %的資料滿足 N < 500000 N<500000 A [ j ] < 1 0 8 A[j]<10^8


題解:

題目太長,先轉述一下題意吧
求使得父節點的權值為子節點的權值之和,且子節點權值相等 的最少的修改次數

由樣例解釋可以發現,到晚上六點時葉子結點為空,其他節點滿,而且每層的容量是相同的

那麼我們有:對於樹上任一個點,其權值一旦確定,整棵樹的權值即可確定。

例如:

在這裡插入圖片描述
從根往下遞推,累計路徑上的權值,如下圖:
圖片來自Luogu BillYang
L u o g u B i l l Y a n g 圖片來自Luogu BillYang

!:不想寫高精用 l o g log 將乘法轉化為加法
l o g a ( M N ) = l o g a M + l o g a N loga(MN)=logaM+logaN

注意精度損失,要判EPS

程式碼:

#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<cmath>
#include<algorithm>
const double Eps=1e-8;
using namespace std;
inline int read(){
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}
int head[500007],cnt;
double a[500007];
int w[500007],out[500007];
struct Edge{int next,to;}edge[1000007];
inline void add_edge(int from,int to){
	edge[++cnt].next=head[from];
	edge[cnt].to=to;head[from]=cnt;
}
void dfs(int x,int fa,double sum){
	a[x]=sum+log(w[x]);
	for(int i=head[x];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;out[x]++;//統計當前節點的初度,也就是每層的節點數
	}
	for(int i=head[x];i;i=edge[i].next){
		int to=edge[i].to;
		if(to==fa)continue;
		dfs(to,x,sum+log(out[x]));
	}
}
int main(){
	int n=read(),lll=1,ans=1;
	for(int i=1;i<=n;++i)w[i]=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		add_edge(u,v);add_edge(v,u);
	}
	dfs(1,0,log(1));
	sort(a+1,a+n+1);
	for(int i=2;i<=n;++i)if(a[i]-a[i-1]<Eps)ans=max(ans,++lll);else lll=1;
	printf("%d",n-ans);
	return 0;
}