AcWing 给树染色
Posted bigyellowdog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AcWing 给树染色相关的知识,希望对你有一定的参考价值。
AcWing 给树染色
Description
一颗树有 n 个节点,这些节点被标号为:1,2,3…n,每个节点 i 都有一个权值 A[i]。
现在要把这棵树的节点全部染色,染色的规则是:
根节点R可以随时被染色;对于其他节点,在被染色之前它的父亲节点必须已经染上了色。
每次染色的代价为T*A[i],其中T代表当前是第几次染色。
求把这棵树染色的最小总代价。
Input
第一行包含两个整数 n 和 R ,分别代表树的节点数以及根节点的序号。
第二行包含 n 个整数,代表所有节点的权值,第 i 个数即为第 i 个节点的权值 A[i]。
接下来n-1行,每行包含两个整数 a 和 b ,代表两个节点的序号,两节点满足关系: a 节点是 b 节点的父节点。
除根节点外的其他 n-1 个节点的父节点和它们本身会在这 n-1 行中表示出来。
同一行内的数用空格隔开。
Output
- 输出一个整数,代表把这棵树染色的最小总代价。
Data Size
- 1≤n≤1000,
1≤A[i]≤1000
Sample Input
5 1 1 2 1 2 4 1 2 1 3 2 4 3 5 0 0
Sample Output
33
题解:
- 贪心。
- 妙题,妙不可言。
- 首先如果染色没有限制怎么办?那么肯定先染权值最大的,然后染权值次大的… …跟[打水问题]类似。
- 那么染色有了限制怎么办?那么对于权值最大的结点,如果它父亲被染了,它肯定立即染上颜色。
- 所以,因为权值最大的点和其父亲的染色是连续的,我们就把它们俩看作一个整体,即可以合并成一个新点。
- 那么现在假设有3个点x、y、z,x是y的父亲,y是整棵树里权值最大的点。所以把x、y看作一个整体(x, y)。
- 接下来就有两种染色方案
- (x,y)先染,z后染
- z先染,(x,y)后染
- 两种情况分别的代价是x + 2y + 3z和z + 2x + 3y
- 用“微扰法”,假设(x,y)先染比z先染优,那么就有x + 2y + 3z > z + 2x + 3y
- 化简得到:z < (x + y) / 2。说明如果一个整体的平均值比另一个点大,那么先染这个整体更优。
- 所以到这里我们可以整理出算法流程:每次找出当前权值最大的非根节点,将其染色顺序排在紧随父节点之后的位置,然后将该点合并进父节点中,更新父节点的权值。直到将所有点都合并进根节点为止。
- 得出了染色顺序后,模拟染色即可得出答案。
- 但是这个染色顺序求起来挺麻烦的(
其实也不难吧,用平衡树维护下就好了,我没试过)(也可以过) - 所以我们还可以在合并的时候实时更新ans,O(1)更新,巧妙的设计!(常用的技巧)
- 最初所有点各自为一组,总分值是 S=∑ai?1 (1 <= i <= n)
接下来每次会将两组点合并,将其中一组点接在另一组点的后面。比如两组点分别是 xi和 yi,我们将 yi 接在 xi 之后,则 yi 中每个点所乘的系数均会增加一个相同的偏移量,这个偏移量就是 xi 中点的个数,假设是 k,则合并之后,总的权值直接加上 k?∑yi 即可。
#include <iostream>
#include <cstdio>
#define N 1005
using namespace std;
struct A int sum, size, fat; double avg; a[N];
int n, root, ans;
int find()
int pos; double res = -1;
for(int i = 1; i <= n; i++)
if(i != root && a[i].avg > res)
res = a[i].avg, pos = i;
return pos;
int main()
cin >> n >> root;
for(int i = 1; i <= n; i++)
cin >> a[i].sum;
a[i].size = 1;
a[i].avg = a[i].sum;
ans += a[i].sum;
for(int i = 1; i < n; i++)
int u, v;
cin >> u >> v;
a[v].fat = u;
for(int i = 1; i < n; i++)
int p = find(), father = a[p].fat;
ans += a[father].size * a[p].sum;
a[father].sum += a[p].sum;
a[father].size += a[p].size;
a[father].avg = (double)a[father].sum / (double)a[father].size;
a[p].avg = -1;
for(int j = 1; j <= n; j++)
if(a[j].fat == p) a[j].fat = father;
cout << ans;
return 0;
以上是关于AcWing 给树染色的主要内容,如果未能解决你的问题,请参考以下文章