P3642 [APIO2016] 烟花表演

首先容易写出朴素 dp。设状态 \(f_{u,x}\) 表示 \(u\) 子树内同时在时刻 \(x\) 爆炸的最小代价,有转移:

\(f_{u,x}=\sum_{v\in son_u}\min_{y\le x} (f_{v,y}+|W(u,v)-x+y|)\)

发现状态数已经是 \(O(nV)\) 了,而第二维显然是不好去掉的。

\(f_u\) 看作函数 \(f_u(x)\),发现形如下凸包。

叶子节点时 \(f_{u}(0)=1\),而每次转移相当于 \(\min+\) 卷积与对位加,均不破坏凸性。

设函数 \(g_{(u,v)}(x)=|W(u,v)-x|\),则转移形如:

\[f_u(x)=\sum_{v\in son_u} (f_v\star g_{(u,v)})(x) \]

其中 \(\star\) 表示 \(\min+\) 卷积。

考虑 slope trick 维护函数,而两种转移对函数的影响分别为:

  1. 对位加:

折点集合并。

  1. \(\min+\)

两个凸包从差分数组上考虑则每个位置上为合并两个差分数组后前 \(k\) 小数之和,即结果差分数组为两个差分数组归并。

位置 0 上即为所有边权和,凸包的差分数组形如多段值递增,而 \(g(x)=|W|-x\) 的差分数组形如 \(W\) 个 -1 与一段无限长的 1。则差分数组的归并过程能看作维护凸包斜率 0 段 \([l,r]\),将 \(W\) 个 -1 插在 \(l\) 前,一段 1 插在 \(r\) 后。在 slope trick 维护状物中的变化表现为 \(l,r\) 的位置后移 \(W\)。每次将斜率 \(>0\) 的部分弹出即可。可并堆容易实现其中的操作。

Takanashi Rikka
#include<bits/stdc++.h>
using namespace std;
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define il inline
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
inline void cmx(auto &x,ll y){if(y>x)x=y;}
inline void cmn(auto &x,ll y){if(y<x)x=y;}
inline int max(vector<int>w){int res=-1e9;for(int i:w)cmx(res,i);return res;}
#define pcount(x) __builtin_popcount(x)
const int N=7e5+5;
#define int ll
int w[N];
vector<int>e[N];
struct node{int son[2],v,d;}t[N];
#define s(u) (t[t[u].son[1]].d<t[t[u].son[0]].d)
int tot,rt[N];
int build(int x){return t[++tot].v=x,tot;}
int merge(int l,int r){
	if(!l||!r)return l|r;
	// cerr<<l<<' '<<r<<'\n';
	if(t[l].v<t[r].v)swap(l,r);
	t[l].son[s(l)]=merge(t[l].son[s(l)],r),
	t[l].d=t[t[l].son[s(l)]].d+1;
	return l;
}
int del(int u){
	int v=merge(t[u].son[0],t[u].son[1]);
	t[u].d=t[u].son[0]=t[u].son[1]=0;return v;
}
vector<int>q;
il void get(int u){
	q.pb(t[u].v);
	if(t[u].son[0])get(t[u].son[0]);
	if(t[u].son[1])get(t[u].son[1]);
}
void UesugiErii(){
	int n,m;ll ans=0;cin>>n>>m,n+=m;
	for(int i=2,fa;i<=n;i++)
		cin>>fa>>w[i],ans+=w[i],e[fa].pb(i);
	for(int i=n;i;i--){
		if(!e[i].size()){rt[i]=merge(build(w[i]),build(w[i]));continue;}
		for(int v:e[i])rt[i]=merge(rt[i],rt[v]);
		for(int _=e[i].size()-1;_;_--)rt[i]=del(rt[i]);
		if(i>1){
			int R=rt[i];rt[i]=del(rt[i]);
			int L=rt[i];rt[i]=del(rt[i]);
			t[L].v+=w[i],t[R].v+=w[i];
			for(int j:{0,1})
				t[L].son[j]=t[R].son[j]=0;
			rt[i]=merge(rt[i],merge(L,R));
		}
	}
	get(rt[1]);sort(q.begin(),q.end());
	int lst=0,sp=1-q.size();
	for(int i:q)
		ans+=sp*(i-lst),++sp,lst=i;
	cout<<ans;
}
signed main(){
	//IO();
	cfast;
	int _=1;//cin>>_;
	for(;_;_--)UesugiErii();
	return 0;
}
posted @ 2026-01-16 15:28  Uesugi1  阅读(1)  评论(0)    收藏  举报