LGP3642题解

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

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

不难发现有 dp[u][k]=mini=0k(|i+wk|+(vson(u)dp[v][i]))。其中 w 表示父亲到自己的边权。

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

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

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

将边权变短类似于 G[k]=mini=kk+w(F[i]+(ik))

将边权变长类似于 G[k]=mini=0k(F[i]+(ki))

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

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

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

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

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

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

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

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

#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();
}

本文作者:Prean

本文链接:https://www.cnblogs.com/lmpp/p/16722695.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Prean  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
var canShowAdsense=function(){return !!0};
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起