题意:给定一棵树,每个节点有一个权值,现要求给这些节点进行排列,设排列后的节点顺序为v1~vn,它们的权值是w1~wn,那么我们要求一种排列使得w1*1+w2*2+...+wn*n最小。还有一个限制就是这个排列满足每个节点的父节点都排在该结点之前。

分析:试想,如果没有父节点排在节点之前的限制,那么这个题目非常简单,只需要将结点按照权值从大到小排列即可。加上了这个限制之后,如果权值最大的那个节点一旦满足了条件(父节点被排在了之前的某个位置),那么这个权值最大的节点一定要紧挨着这个父节点,即把这个权值最大的节点排在它所能排的最前面的位置。因为对于这个节点如果不受限制应该排在第一位,而有了限制,在满足了限制之后也应把它尽可能地排在前面。所以它一定是挨着父节点的。那么现在在最终的排列中我们确定了两个节点的前后相邻关系,将他们绑定在了一起。

试想如果保持这个相邻关系的同时去掉其他节点的限制,那么我们应该如何排列呢?我们假设绑定在一起的两节点是a和b。现有一个另外的节点x,我们看两种排列xab,abx对最终的计算结果有什么影响。x*i+a*(i+1)+b*(i+2); a*i + b*(i+1) + x*(i+2)。后者减去前者等于2x-(a+b)。即将x从ab之前挪到ab之后,ab各左移1位,结果减小a+b。x右移2位结果增加2x。因此两者谁在前谁在后我们只需要比较a+b和2x即可,也可以比较(a+b)/2和x。

将这个定理进行一下推广,绑定在一起的不一定是两个节点,可以是一个更长的序列,与这个序列进行比较看谁放在前面的也可以是一个序列。设一个序列有n1个节点,第二个序列有n2个节点。那么我们比较两者谁放在前面的时候需要比较的是(n1个权值之和×n2)和(n2个权值之和×n1)。即左移和右移产生的结果变化。当然也可以比较(n1个权值之和/n1)和(n2个权值之和/n2)。

我们可以再次进行推广,如果我们要排列的不是节点,而是许多序列的话,那么我们只需要计算每个序列权值的平均数(例如:n个节点的序列,要计算n个权值之和/n),然后按照这个平均数从大到小排列即可使得计算结果最小。这样就可以让序列与节点有了一个统一的衡量值——平均数。

这样一来,我们就可以将上面的绑定两节点的操作看成是将问题规模缩小的操作,在帮定两节点的同时我们在树中也将两节点合并,变为一个节点,即将子节点的孩子变为父节点的孩子。然后合并后的节点的权值是合并在这个节点中的所有节点的权值的平均数。我们成功的将问题规模减小了1。只需要不断这样做即可将问题缩减为只有一个节点。

实现过程中,应在树中每个节点记录并入该节点的个数和权值和。树的存储与绑定前后关系的记录要分开存储,用一个数组单独记录前后绑定的排序关系。

#include <cstdio>
#include <cstring>
using namespace std; #define MAX_NODE_NUM 1005 struct Node
{
int value;
int father;
int next;
int child;
int id;
int num;
}node[MAX_NODE_NUM]; int root_id, node_num;
bool vis[MAX_NODE_NUM];
int link[MAX_NODE_NUM];
int original_value[MAX_NODE_NUM]; void add_edge(int father, int son)
{
int temp = node[father].child;
node[father].child = son;
node[son].next = temp;
node[son].father = father;
} void input()
{
memset(node, -, sizeof(node));
for (int i = ; i < node_num; i++)
{
scanf("%d", &node[i].value);
node[i].id = i;
node[i].num = ;
original_value[i] = node[i].value;
}
for (int i = ; i < node_num - ; i++)
{
int node_a, node_b;
scanf("%d%d", &node_a, &node_b);
node_a--;
node_b--;
add_edge(node_a, node_b);
}
} void merge_with_father(int id)
{
int father = node[id].father;
node[father].value += node[id].value;
node[father].num += node[id].num;
node[father].id = node[id].id;
int end = id;
while (node[end].next != -)
end = node[end].next;
node[end].next = node[id].child;
while (node[end].next != -)
{
end = node[end].next;
node[end].father = father;
}
//printf("father:%d\tson:%d\n", father + 1, id + 1);
} bool larger(int a, int b)
{
if (a == -)
return true;
return node[a].value * node[b].num < node[b].value * node[a].num;
} void work()
{
memset(vis, , sizeof(vis));
vis[root_id] = true;
while ()
{
int p = -;
for (int i = ; i < node_num; i++)
if (!vis[i] && larger(p, i))
p = i;
if (p == -)
break;
vis[p] = true;
link[node[node[p].father].id] = p;
// printf("a:%d\tb:%d\n", node[node[p].father].id + 1, p + 1);
merge_with_father(p);
}
int temp = root_id;
int ans = ;
for (int i = ; i <= node_num; i++)
{
// printf("%d ", temp + 1);
ans += i * original_value[temp];
temp = link[temp];
}
printf("%d\n", ans);
} int main()
{
while (scanf("%d%d", &node_num, &root_id), node_num | root_id)
{
root_id--;
input();
work();
}
return ;
}

poj2054的更多相关文章

  1. [POJ2054]Color a Tree (并查集+贪心)

    POJ终于修好啦 题意 和UVA1205是同一题,在洛谷上是紫题 有一棵树,需要给其所有节点染色,每个点染色所需的时间是一样的都是11.给每个点染色,还有一个开销“当前时间×ci×ci”,cici是每 ...

  2. poj2054 Color a Tree

    神题.这题是巨毒瘤... 自己写真可谓是: 排空驭气奔如电,上天入地求之遍 上穷碧落下黄泉,两处茫茫皆不见 由于我们知道:不是树形时,不停选值最大的节点可以得到最小代价. 那么我们就能想出一个错误的贪 ...

  3. Poj2054 color a tree && [HNOI/AHOI2018]排列

    https://zybuluo.com/ysner/note/1120723 题面 原题 某省选强化题 大致意思是给你一颗树,选父亲后才能选儿子. 每个点对答案的贡献为你在第几次选这个点 × 该点权值 ...

  4. $Poj2054\ Color\ a\ Tree\ $ 贪心

    $poj$ $Description$ 一颗树有 $n$ 个节点,这些节点被标号为:$1,2,3…n,$每个节点 $i$ 都有一个权值 $A[i]$. 现在要把这棵树的节点全部染色,染色的规则是: 根 ...

  5. 0x07 贪心

    被虐爆了...贪心这种玄学东西还可以证吗??除了范围缩放算是可以想想比较经典(倍增第一题?)... poj3614:这道题想了很久,并没有想到是把minSPF按大到小排序,一直的思想是小的就小到大排序 ...

随机推荐

  1. Software-Defined Networking:A Comprehensive Survey--Day2

    Software-Defined Networking:A Comprehensive Survey (续+1s) IV. SOFTWARE-DEFINED NETWORKS: BOTTOM-UP S ...

  2. Alpha冲刺——day4

    Alpha冲刺--day4 作业链接 Alpha冲刺随笔集 github地址 团队成员 031602636 许舒玲(队长) 031602237 吴杰婷 031602220 雷博浩 031602634 ...

  3. Java之List排序功能举例

    package test_demo; import java.util.ArrayList; import java.util.Collections; import java.util.List; ...

  4. 首先java中集合类主要有两大分支

    本文仅分析部分原理和集合类的特点,不分析源码,旨在对java的集合类有一个整体的认识,理解各个不同类的关联和区别,让大家在不同的环境下学会选择不同的类来处理. Java中的集合类包含的内容很多而且很重 ...

  5. 【洛谷P2661】信息传递

    题目大意:给定一个有 N 个点,N 条边且每个点的出度均为 1 的有向图,求该有向图的一个最小环. 题解:由于每个点的出度均为 1,可知可能的情况只有以下几种:一个环或多个环,一个环+一条链.因此,可 ...

  6. Linux下使用cron让Python程序持久化运行

    正常情况下,一个python程序如果希望实现一直运行,不出错不奔溃是很难的,即使编译为可持续文件也是一样 幸运的是很多需求并不是需要24小时不间断运行,而是每隔一段时间运行一次即可 Linux系统自带 ...

  7. spring boot 分布式事务实现(XA方式)

    关于spring boot 支持分布式事务,XA是常用的一种方式. 这里把相关的配置记下,方便以后使用. 首先配置两个不同的数据源 : 订单库.持仓库. /** * Created by zhangj ...

  8. Jenkins 01——简介

    Jenkins是一个开源软件项目,一个可扩展的持续集成引擎.旨在提供一个开放易用的软件平台,使软件的持续集成变成可能. 持续集成是一种开发实践,需要开发人员定期将代码集成到共享存储库中.这个概念意在消 ...

  9. bzoj千题计划293:bzoj3142: [Hnoi2013]数列

    http://www.lydsy.com/JudgeOnline/problem.php?id=3142 如果已知数列的差分数列a[1]~a[k-1] 那么这种差分方式对答案的贡献为 N-Σ a[i] ...

  10. bzoj千题计划231:bzoj1997: [Hnoi2010]Planar

    http://www.lydsy.com/JudgeOnline/problem.php?id=1997 如果两条边在环内相交,那么一定也在环外相交 所以环内相交的两条边,必须一条在环内,一条在环外 ...