[luogu3237 HNOI2014] 米特运输 (树形dp)

传送门

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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。 博主:https://www.cnblogs.com/Menteur-Hxy/
原文地址:https://www.cnblogs.com/Menteur-Hxy/p/9347769.html