luogu P3642 [APIO2016]烟火表演
题面传送门
首先我们有一个高达26pts的dp:设\(dp_{x,i}\)为将\(x\)子树内全部变成\(x\)的最小代价。合并一下即可转移。
考虑优化,容易发现我们这个dp的函数图像是一个下凸的分段一次函数。我们考虑将其合并到父亲的时候会发生什么。
首先设斜率为\(0\)的一段为\([L,R]\),显然这一段是最小的,且这个函数的斜率为整数,也即绝对值至少为\(1\)。首先在\(R\)右边的一段显然是一个斜率为\(1\)的函数,因为这条到父亲的边可以无限延长。
然后原来\([L,R]\)的就加上长度\(l\)后不动,在\([L-l,L]\)区间的显然是一条斜率为\(-1\)的直线,然后\(L-l\)的值加上\(l\)后不动即可。
我们考虑用一个可并堆维护拐点,合并两个函数的时候就直接合并,然后在父亲的时候特殊处理。
考虑第一步:先将后面的权值设为\(-1\),设\(x\)点的儿子数为\(son_x\),则我们从\(x\)的儿子合并了\(son_x\)个代表\(-1\)权值的拐点,则删去\(son_x-1\)个即可。
然后第二步:将斜率为\(0\)的部分右移\(l\),则删掉最后两个点,然后加上增加后的两个点即可。
第三步:将\([L-l,L]\)部分设为\(1\),然后我们发现在第二步的时候好像已经表达了这个意思了。
于是我们通过维护拐点成功得到了整棵树的函数。
但是我们必须要有一个点才能通过拐点还原出一整个函数啊。
容易发现\(0\)处的函数值很好计算,也就是所有边权之和,于是这样就可以反推出最小值坐标了。
时间复杂度\(O(n\log n)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (600000+5)
#define M ((N<<2)+5)
#define K (350)
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;struct Edge{int to,w;};vector<Edge> S[N];
int n,m,x,y,H,Rt[N];ll Ans,B[N];
namespace LT{
int L[N],R[N],H[N],cnt;ll Val[N];I void Up(int v){H[L[v]]>H[R[v]]&&(swap(L[v],R[v]),0);H[v]=H[L[v]]+1;}
I int Merge(int x,int y){if(!x||!y) return x|y;Val[x]<Val[y]&&(swap(x,y),0);L[x]=Merge(L[x],y);Up(x);return x;}
I int NN(ll x){H[++cnt]=1;Val[cnt]=x;return cnt;}I void pop(int &x){x=Merge(L[x],R[x]);}
}
I void dfs(int x,int La){
if(!S[x].size()) {Rt[x]=LT::Merge(LT::NN(La),LT::NN(La));return;}for(Edge i:S[x]) dfs(i.to,i.w),Rt[x]=LT::Merge(Rt[x],Rt[i.to]);
for(int i=1;i<S[x].size();i++) LT::pop(Rt[x]);if(x==1) return;ll z,y;z=LT::Val[Rt[x]];LT::pop(Rt[x]);y=LT::Val[Rt[x]];LT::pop(Rt[x]);Rt[x]=LT::Merge(Rt[x],LT::Merge(LT::NN(z+La),LT::NN(y+La)));
}
int main(){
freopen("1.in","r",stdin);
int i,j;scanf("%d%d",&n,&m);for(i=2;i<=n+m;i++) scanf("%d%d",&x,&y),S[x].PB((Edge){i,y}),Ans+=y;dfs(1,0);while(Rt[1]) B[++H]=LT::Val[Rt[1]],LT::pop(Rt[1]);
for(i=H+1;i>=3;i--) Ans-=(i-2)*(B[i-1]-B[i]);printf("%lld\n",Ans);
}

浙公网安备 33010602011771号