CF1088F Ehab and a weird weight formula 贪心 倍增

CF1088F Ehab and a weird weight formula

题意

给定一棵树,点有点权,其中这棵树满足除了权值最小的点外,每个点至少有一个点权小于它的相邻点。
要求你重新构建这棵树,使得代价最小。计算代价的方法如下:

  • 点的代价: (deg_xv_x),其中(deg_x)表示点(x)的度
  • ((x, y))的代价:(log_2(dis(x, y)) cdot min(v_x, v_y)),其中(dis(x, y))表示(x)(y)在原树中的距离。

树的代价为点的代价与边的代价之和。

题解

首先我们来证明一个事情:为了满足每个点至少有一个点权小于它的相邻点(除了权值最小的点之外),这棵树必须满足:根为权值最小的点,对于每个点而言,父亲的权值一定小于它。
如果一个点x要依靠它的儿子y来满足这个性质,那么就意味着它的儿子y也需要依靠它的某个儿子z来满足这个性质。
那么这样的话,叶节点就无论如何都不能满足这个性质了。得证。

那么这个性质可以为我们带来什么?
首先我们考虑不断插入节点来构建出这棵树。那么一个点(x)选择(fa)作为他的父亲,带来的贡献为:
(v_x + v_{fa} + log_2(dis(x, fa)) cdot min(v_xv_{fa}))
如果一个点(x)选中了它子树中的某个点作为父亲,那么它还不如选择距离更小的祖先,代价更小。
如果一个点(x)选中了深度小于它的非祖先节点,那么它还不如从它选择的这个节点往上走,走到一个离它最近的,是(x)的祖先的节点,那么这个新节点肯定比原来选择的节点距离(x)更近,且权值更小,所以显然更优。
因此点(x)一定会选择它的某个祖先,因为(log_2)的值可以看做是一段一段的,我们可以用倍增来维护每一段中的最小值,而因为越往上越小,所以这个最小值刚好就是它的第(2^k)级祖先,因此对于某个点,我们只需要倍增的跳一跳,在这些可能的点中选择一个最优的就可以了。
当然如果祖先的个数不够(2^k)的话就需要取root作为father

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 500100
#define ac 1000100
#define inf 100000000000000000LL

int n, rot;
LL ans;
int v[AC], fa[AC][20];
int Head[AC], date[ac], Next[ac], tot;

inline int read()
{
    int x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

inline void upmin(LL &a, LL b) {if(b < a) a = b;}
inline void upmax(LL &a, LL b) {if(b > a) a = b;}

inline void add(int f, int w)
{
    date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
    date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot;
}

void pre()
{
    n = read(), rot = 1;
    for(R i = 1; i <= n; i ++) 
    {
        v[i] = read();
        if(v[i] < v[rot]) rot = i;
    }
    for(R i = 2; i <= n; i ++) add(read(), read());
}

void dfs(int x)
{
    LL rnt = inf;
    if(x != rot)
    {	
        for(R i = 0; i <= 19; i ++) 
        {	
            if(i) fa[x][i] = fa[fa[x][i - 1]][i - 1];
            upmin(rnt, 1LL * (i + 1) * v[fa[x][i]] + v[x]);
        }
        ans += rnt;
    }
    else for(R i = 0; i <= 19; i ++) fa[x][i] = x;
    for(R i = Head[x]; i; i = Next[i])
    {
        int now = date[i];
        if(now == fa[x][0]) continue;
        fa[now][0] = x, dfs(now);
    }
}

int main()
{
//	freopen("in.in", "r", stdin);
    pre();
    dfs(rot);
    printf("%lld
", ans);
//	fclose(stdin);
    return 0;
}
原文地址:https://www.cnblogs.com/ww3113306/p/10551913.html