luogu P3642 [APIO2016]烟火表演
https://www.luogu.com.cn/problem/P3642
好毒瘤啊!!!
首先按照套路
设
f
(
x
)
表
示
以
u
为
根
的
,
距
离
为
x
的
最
小
代
价
设f(x)表示以u为根的,距离为x的最小代价
设f(x)表示以u为根的,距离为x的最小代价
然后发现这是一个下凸函数
然后分情况讨论一下(4种)
这位大佬讲得很好:blog
然后拿个可并堆搞一搞就行了
我用的是左偏树
code:
#include<bits/stdc++.h>
#define N 1200050
#define ll long long
using namespace std;
int ch[N][2], dis[N];
ll val[N];
int merge(int x, int y) {
if(!x || !y) return x + y;
if(val[x] < val[y]) swap(x, y);
ch[x][1] = merge(ch[x][1], y);
if(dis[ch[x][0]] < dis[ch[x][1]]) swap(ch[x][0], ch[x][1]);
dis[x] = dis[ch[x][1]] + 1;
return x;
}
int pop(int x) {
return merge(ch[x][0], ch[x][1]);
}
int n, m, fa[N], a[N], in[N], rt[N], tot;
int main() {
scanf("%d%d", &n, &m);
ll ans = 0;
for(int i = 2; i <= n + m; i ++) {
scanf("%d%d", &fa[i], &a[i]);
ans += a[i]; in[fa[i]] ++;
}
for(int u = n + m; u > 1; u --) {
ll L = 0, R = 0;
if(u <= n) while(-- in[u]) rt[u] = pop(rt[u]);
R = val[rt[u]]; rt[u] = pop(rt[u]);
L = val[rt[u]]; rt[u] = pop(rt[u]);
val[++ tot] = L + a[u], val[++ tot] = R + a[u];
rt[u] = merge(rt[u], merge(tot - 1, tot));
rt[fa[u]] = merge(rt[fa[u]], rt[u]);
}
while(in[1] --) rt[1] = pop(rt[1]);
while(rt[1]) ans -= val[rt[1]], rt[1] = pop(rt[1]);
printf("%lld", ans);
return 0;
}
好妙啊,学会了一种新的转换