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;
}