Description
给你一棵树有
n
n
个非叶子节点以及个叶子节点,以
1
1
为根,每条边有边权,现在请你改变一些边的权(不可为负),使得根到各个叶子节点所经过的路径长度相等。代价为目标边权与原边权之差的绝对值,求最小代价。(,
1≤wi≤109
1
≤
w
i
≤
10
9
)
Solution
这题我感觉巨强!
顺便学习了一下左偏树(太蒟了
不过为啥是凸性呢,好像按照这个理论推下去就是也满足了前面的猜想(好勉强啊qwq
最后答案的统计的话,可以理解为每次拿一个拐点出来,这个函数图像的斜率就减一,那么我们直接在
sum
s
u
m
中减掉现在的横坐标,这样的话也把上面的我所影响的也一起减掉了。
Source
//2018-4-20
//miaomiao
//
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define Forr(i, a, b) for(int i = (a); i >= (int)(b); --i)
#define N (600000 + 5)
struct LeftTree{
int lc, rc, dis;
LL v;
}tr[N];
int n, m, tot, fa[N], w[N], dig[N], rt[N];
int Merge(int a, int b){
if(!a || !b) return a + b;
if(tr[a].v < tr[b].v) swap(a, b);
tr[a].rc = Merge(tr[a].rc, b);
if(tr[tr[a].lc].dis < tr[tr[a].rc].dis) swap(tr[a].lc, tr[a].rc);
if(!tr[a].rc) tr[a].dis = 0; else tr[a].dis = tr[tr[a].rc].dis + 1;
return a;
}
int Pop(int now){
return Merge(tr[now].lc, tr[now].rc);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("firework.in", "r", stdin);
freopen("firework.out", "w", stdout);
#endif
LL l, r, sum = 0;
scanf("%d%d", &n, &m);
For(i, 2, n + m){
scanf("%d%d", &fa[i], &w[i]);
sum += w[i]; ++dig[fa[i]];
}
Forr(i, n + m, 2){
l = r = 0;
if(i <= n){
while(--dig[i]) rt[i] = Pop(rt[i]);
r = tr[rt[i]].v; rt[i] = Pop(rt[i]);
l = tr[rt[i]].v; rt[i] = Pop(rt[i]);
}
tr[++tot].v = l + w[i], tr[++tot].v = r + w[i];
rt[i] = Merge(rt[i], Merge(tot - 1, tot));
rt[fa[i]] = Merge(rt[fa[i]], rt[i]);
}
while(dig[1]--) rt[1] = Pop(rt[1]);
while(rt[1]) sum -= tr[rt[1]].v, rt[1] = Pop(rt[1]);
printf("%lld\n", sum);
return 0;
}