luogu P3642 [APIO2016]烟火表演

https://www.luogu.com.cn/problem/P3642

好毒瘤啊!!!
首先按照套路
设 f ( x ) 表 示 以 u 为 根 的 , 距 离 为 x 的 最 小 代 价 设f(x)表示以u为根的,距离为x的最小代价 f(x)ux
然后发现这是一个下凸函数
然后分情况讨论一下(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;
}

好妙啊,学会了一种新的转换

posted @ 2021-04-05 21:47  lahlah  阅读(27)  评论(0编辑  收藏  举报