LGP3642题解

whk 了两天,来口胡一道题放松放松。

\(dp[u][k]\) 表示子树中所有叶子都距离自己的父亲节点长度为 \(k\) 的最小代价。(定义 \(1\) 号节点的父亲节点为 \(0\),距离为 \(0\)

不难发现有 \(dp[u][k]=\min_{i=0}^{k}(|i+w-k|+(\sum_{v\in son(u)}dp[v][i]))\)。其中 \(w\) 表示父亲到自己的边权。

我们可以证明,若 \(F_u(x)=dp[u][x]\),那么 \(F_u\) 是一个分段一次函数,并且是一个单谷函数。

对于后面的 \(\sum\) 可以看做是将若干分段函数加起来。将分段函数求导后会单调不增,加起来后一定仍然单调不增。

对于前面的加绝对值,可以把将边权变长和将边权变短分开讨论。

将边权变短类似于 \(G[k]=\min_{i=k}^{k+w}(F[i]+(i-k))\)

将边权变长类似于 \(G[k]=\min_{i=0}^{k}(F[i]+(k-i))\)

显然可以先变长再变短,这样是无所谓的。

可以发现边权变短相当于是在谷点的前面加了一段斜率为 \(1\) 长度为 \(w\) 的一次函数,变长相当于是把斜率对 \(1\)\(\min\)

使用平衡树维护分段函数,只维护分段函数的斜率以及其在 \(0\) 处的值(其实就是边权之和),复杂度 \(O(n\log^2n)\)。不知道 16 年的机子能不能跑。

但是当分段函数特殊时是可以使用堆维护分段点的,表示每经过一个分段点斜率就减小 \(1\)

很显然一次操作结束后的分段函数最右边那个分段点代表的是右边斜率为 \(1\)

于是可以使用可并堆维护分段点的最大值,每次弹出最大的两个然后将弹出值加上 \(w\) 后再插回去即是将边权变短的操作。

然后再 pop 儿子数量-1 个分割点即可使最右侧的斜率为 \(1\)

只需要写左偏树,且复杂度为 \(O(n\log n)\)

#include<cstdio>
#include<cctype>
namespace SOLVE{
	inline int read(){
		int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
	}
	typedef long long ll;
	const int M=3e5+5;
	int n,ege,h[M];int tot,d[M<<1],ls[M<<1],rs[M<<1];ll w[M<<1];
	struct Edge{
		int v,nx;ll w;
	}e[M];
	inline void swap(int&a,int&b){
		int c=a;a=b;b=c;
	}
	inline void Add(const int&u,const int&v,const ll&w){
		e[++ege]=(Edge){v,h[u],w};h[u]=ege;
	}
	inline int merge(const int&q,const int&p){
		if(!q||!p)return q|p;if(w[q]<w[p])return merge(p,q);
		rs[q]=merge(rs[q],p);if(d[rs[q]]>d[ls[q]])swap(ls[q],rs[q]);d[q]=d[rs[q]]+1;return q;
	}
	inline int DFS(const int&u,const int&V){
		int rt(0),cnt(-1);for(int E=h[u];E;E=e[E].nx)rt=merge(rt,DFS(e[E].v,e[E].w)),++cnt;
		if(~cnt)while(cnt--)rt=merge(ls[rt],rs[rt]);if(u==1)return rt;
		const ll&X=w[rt]+V;rt=merge(ls[rt],rs[rt]);const ll&Y=w[rt]+V;rt=merge(ls[rt],rs[rt]);
		w[++tot]=X;d[tot]=1;rt=merge(rt,tot);w[++tot]=Y;d[tot]=1;rt=merge(rt,tot);return rt;
	}
	inline void main(){
		ll sum(0);n=read()+read();for(int f,w,i=2;i<=n;++i)f=read(),w=read(),Add(f,i,w),sum+=w;
		int rt=DFS(1,0);while(rt=merge(ls[rt],rs[rt]))sum-=w[rt];printf("%lld",sum);
	}
}
signed main(){
	SOLVE::main();
}
posted @ 2022-09-23 14:36  Prean  阅读(27)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};