zoukankan      html  css  js  c++  java
  • 【BZOJ3573】【HNOI2014】米特运输

    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

    题面这么长。。。
    其实就是你要使得每个节点的子节点权值相等,并且该节点的权值=子节点个数*子节点权值。
    如果暴力确定某一个点不改变的话。。。复杂度是 O(n)的,一共n次,显然不可接受。。。
    事实上,是可以发现,某个节点权值不改变,同样不需要改变的节点的权值是同一个集合。。。
    然后考虑如何找这个集合,显然结果就是n-最大集合的元素个数。。。
    显然算出这个集合是相对比较容易的,考虑节点i的值与根节点的关系,显然的,根节点的值应为(vi*Pi sz [fa[i][j]]),其中,sz表示某节点的子节点个数,fa表示某节点的若干个祖先,显然累乘是可以边递归边得到的,因此只需要维护每个点一定时,根节点的权值即可,最后排序一下求出集合,统计答案。注意事项:显然累乘是大到爆炸的,高精度?不存在的。由于不需要输出结果,可以取对数化乘为加。时间复杂度(O(n log n))

    Code

    #include <math.h>
    #include <stdio.h>
    #include <algorithm>
    #define MN 500005
    #define eps 1e-9
    #define R register
    #define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
    #define end fclose(stdin);fclose(stdout)
    inline int read(){
    	R int x; R bool f; R char c;
    	for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
    	for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
    	return f?-x:x;
    }
    int to[MN],nxt[MN],h[MN],v[MN],n,sz[MN],en,ans,sum; double f[MN];
    inline void ins(int x,int y){to[++en]=y,nxt[en]=h[x],h[x]=en,++sz[x];}
    inline void dfs(int u,double p){
    	f[u]=p+log(v[u]);
    	for (R int i=h[u]; i; i=nxt[i])
    		dfs(to[i],p+log(sz[u]));
    }
    int main(){
    	n=read();for (R int i=1; i<=n; ++i) v[i]=read();
    	for (R int i=1,x,y; i<n; ++i) x=read(),y=read(),ins(x,y);
    	dfs(1,0.0);std::sort(f+1,f+n+1);
    	for (R int i=1; i<=n; ++i)
    		if (f[i]-f[i-1]<eps) ++sum;
    		else (ans<sum?ans=sum:0),sum=1;
    	ans=ans<sum?sum:ans;printf("%d
    ",n-ans);return 0;
    }
    
  • 相关阅读:
    XamarinSQLite教程在Xamarin.iOS项目中定位数据库文件
    在Xamarin.iOS项目中使用预设数据库
    函数封装多个不同按钮的点击事件
    ajax请求数据动态渲染表格
    计算历时长度
    layui单文件上传
    滚动到顶部固定
    下载
    第一个项目技术总结
    单选框
  • 原文地址:https://www.cnblogs.com/Melacau/p/BZOJ3573.html
Copyright © 2011-2022 走看看