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