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 维护函数,而两种转移对函数的影响分别为:
- 对位加:
折点集合并。
- \(\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;
}

浙公网安备 33010602011771号