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)。
  • 接下来就有两种染色方案
    1. (x,y)先染,z后染
    2. 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 给树染色的主要内容,如果未能解决你的问题,请参考以下文章

Acwing第 58 场周赛完结

Acwing第 58 场周赛完结

Acwing第 58 场周赛完结

Acwing第 25 场周赛完结

AcWing 2060. 奶牛选美(DFS)

四个点的圈是二分图吗