zoukankan      html  css  js  c++  java
  • [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 - MIN);
    	return 0;
    }
    
  • 相关阅读:
    Java编辑PDF写入文字 插入图片
    Java图片压缩
    Java base64 图片编码转换
    JAVA操作字符串
    JAVA获取文件夹下所有的文件
    IntelliJ IDEA 注释模板设置
    IntelliJ IDEA 添加junit插件
    python操作mysql数据库系列-安装MySql
    python操作mysql数据库系列-安装MySQLdb
    软件测试工程师为什么要不断提高自身技能?
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/14009790.html
Copyright © 2011-2022 走看看