1. 程式人生 > >[luogu3237 HNOI2014] 米特運輸 (樹形dp)

[luogu3237 HNOI2014] 米特運輸 (樹形dp)

直接 -i 子節點 map air push 文件 那種 while

傳送門

Description

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

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

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

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

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

Input

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

Output

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

Sample Input

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

Sample Output

3

HINT

一個最優解是將A[1]改成8,A[3]改成4,A[5]改成2。這樣,2和3運給1的量相等,4和5運

給2的量相等,且每天晚上六點的時候,1,2滿,3,4,5空,滿足所有限制條件。

對於100%的數據滿足N<500000,A[j]<10^8

Solution

題面很長實際上就是給一棵有點權的樹,然後要求修改最少的點權使
樹滿足同一個父節點的所有子節點權值均相同
父節點的權值等於它的子節點權值之和
另外權值給的整數但可以修改為小數
怎麽做呢?
首先我們發現當這棵樹的一個點的點權確定後其他點的點權都能算出來(因為樹的結構固定)
那麽我們只需要求出這棵樹中那些點是同一種情況(即能被互相求出)
於是就會想到把每一個節點的權值都表示為通過這個節點算出來的根節點的值
這樣通過比較每個節點算出的根節點是否相同便能確定是否為一種情況
然後統計出那種情況的節點數最多即為最後要變成的情況
PS:要是直接求出根節點權值有可能爆long long 需要用log轉化為加法qwq

Code

//By Menteur_Hxy
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define F(i,a,b) for(register int i=(a);i<=(b);i++)
using namespace std;
typedef long long LL;

LL read() {
    LL x=0,f=1; char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f; c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x*f;
}
 
const int N=500010;
int n,ans;
long double da[N];
vector <int> V[N];

void dfs(int x,long double k) {
    da[x]+=k;
    int siz=V[x].size();
    F(i,0,siz-1) dfs(V[x][i],k+log(siz));
}

int main() {
    n=read();
    F(i,1,n) da[i]+=log(read());
    F(i,1,n-1) {
        int a=read(),b=read();
        V[a].push_back(b);
    }
    dfs(1,0);
    sort(da+1,da+1+n);
    int tmp=1;
    F(i,2,n)  {
        if(da[i]-da[i-1]<=1e-8) tmp++;
        else tmp=1;
        ans=max(ans,tmp);
    }
    printf("%d",n-ans);
    return 0;
}

還有一個作死用stl結果爆精度的代碼qwq
下面的是double60分QAQ
才不會告訴你long double45分

Code

//By Menteur_Hxy
#include<map>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define F(i,a,b) for(register int i=(a);i<=(b);i++)
using namespace std;
typedef long long LL;
typedef pair<double,int> PDI;

LL read() {
    LL x=0,f=1; char c=getchar();
    while(!isdigit(c)) {if(c=='-')f=-f; c=getchar();}
    while(isdigit(c)) x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x*f;
}
 
const int N=500010;
int n;
double da[N];
vector <int> V[N];
map <double,int> M;

void dfs(int x,double k) {
    M[da[x]+k]++;
    int siz=V[x].size();
    F(i,0,siz-1) dfs(V[x][i],k+log2(siz));
}

bool cmp(PDI x,PDI y) {return x.second>y.second;}

int main() {
    n=read();
    F(i,1,n) da[i]+=log2(read());
    F(i,1,n-1) {
        int a=read(),b=read();
        V[a].push_back(b);
    }
    dfs(1,0);
    vector<PDI> A(M.begin(),M.end());
    sort(A.begin(),A.end(),cmp);
//  int siz=A.size();
//  for(register int i=0;i<siz;i++) printf("%d",A[i].second);
    printf("%d",n-A[0].second);
    return 0;
}

[luogu3237 HNOI2014] 米特運輸 (樹形dp)