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\)級祖先,因此對於某個點,我們只需要倍增的跳一跳,在這些可能的點中選擇一個最優的就可以了。

#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\n", ans);
//  fclose(stdin);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章