洛谷P3237 [HNOI2014]米特运输 题解

洛谷P3237 [HNOI2014]米特运输 题解

题目链接:P3237 [HNOI2014]米特运输

题意

这题面是真的长啊 qwq

米特是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星人已经建立好高速通道,每个城市也有了一定储存容量的米特储存器。为了满足上面的限制条件,可能需要重建一些城市中的米特储存器。你可以,也只能,将某一座城市(包括首都)中原来存在的米特储存器摧毁,再新建一座任意容量的新的米特储存器,其容量可以是小数(在输入数据中,储存器原始容量是正整数,但重建后可以是小数),不能是负数或零,使得需要被重建的米特储存器的数目尽量少。

题目讲了半天其实就是说

  • 给定一棵树,树上每个结点有权值

  • 求最少修改结点权值的次数,使得 val [ u ] = ∑ val [ v ] \text{val}[u]=\sum \text{val}[v] val[u]=val[v]

    其中 v v v u u u 的儿子。

不知道谁把它放在树形dp题单里的(恼

手推一下就可以发现,只要确定了一个结点,其他所有的结点都可以确定了

考虑把每个结点确定后形成的树都给算出来

然后在这一堆树里面找到相同树的个数的最大值 mx \text{mx} mx

答案就是 n − mx n-\text{mx} nmx

这个树怎么算呢,这里有一张图,来自link

在这里插入图片描述

即设 f [ u ] f[u] f[u] u u u 所形成的树的"特征值",则有
f [ u ] = ∏ sz [ d ] × val [ u ] f[u]=\prod \text{sz}[d] \times \text{val}[u] f[u]=sz[d]×val[u]
其中 d d d u u u 的祖先结点。

似乎只要把这个值算出来就好了,不过它会爆long long

考虑用 log ⁡ \log log 转化计算( log ⁡ ( a b ) = log ⁡ a + log ⁡ b \log (ab) = \log a + \log b log(ab)=loga+logb

然后比较的时候只要排序一下就好了

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
#define int long long
#define double long double
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(5e5+15)
double f[N];
vector<int> vec[N];
int n,val[N],cnt=1,mn,sz[N],fa[N];
const double eps=1e-13;
int dcmp(double x)
{if(fabs(x)<=eps)return 0;return x>eps?1:-1;}
void dfs1(int u,int f)
{
    fa[u]=f;sz[u]=0;
    for(int v : vec[u])
        if(v!=f)sz[u]++,dfs1(v,u);
}
void dfs2(int u,double sum)
{
    f[u]=sum+log((double)val[u]);
    for(int v : vec[u])
    {
        if(v==fa[u])continue;
        dfs2(v,sum+log((double)sz[u]));
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n; mn=n-1;
    for(int i=1; i<=n; i++)
        cin >> val[i];
    for(int i=1,u,v; i<n; i++)
    {
        cin >> u >> v;
        vec[u].push_back(v);
        vec[v].push_back(u);
    }
    dfs1(1,1);dfs2(1,log(1.0));
    sort(f+1,f+1+n);
    for(int i=1; i<n; i++)
    {
        if(!dcmp(f[i]-f[i+1]))
            ++cnt,mn=min(mn,n-cnt);
        else cnt=1;
    }
    cout << mn;
    return 0;
}

转载请说明出处

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值