LibreOJ #3052. 「十二省联考 2019」春节十二响 长链剖分

题意

给一棵树,点有点权,要求把所有节点分成若干个集合,使得同一集合中任意两点不存在祖先关系,且每一集合的最大点权的和最小。
n ≤ 2 ∗ 1 0 5 n\le2*10^5 n2105

分析

考虑递归处理,假设当前节点的两棵子树都已处理好,那么合并两棵子树的方式为最大值和最大值合并,次大值和次大值合并,如此类推。
可以用长链剖分+优先队列来优化到 O ( n l o g n ) O(nlogn) O(nlogn)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>

typedef long long LL;

const int N=200005;

int n,a[N],cnt,last[N],sz,id[N],son[N],dep[N],tmp[N];
struct edge{int to,next;}e[N];
std::priority_queue<int> que[N];

void addedge(int u,int v)
{
	e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
}

void pre(int x)
{
	dep[x]=1;
	for (int i=last[x];i;i=e[i].next)
		pre(e[i].to),dep[x]=std::max(dep[x],dep[e[i].to]+1);
	for (int i=last[x];i;i=e[i].next)
		if (dep[e[i].to]+1==dep[x]) son[x]=e[i].to;
}

void dfs(int x)
{
	if (son[x])
	{
		dfs(son[x]);
		id[x]=id[son[x]];
	}
	for (int i=last[x];i;i=e[i].next)
	{
		if (e[i].to==son[x]) continue;
		dfs(e[i].to);
		int tot=0;
		while (!que[id[e[i].to]].empty())
		{
			tmp[++tot]=std::max(que[id[x]].top(),que[id[e[i].to]].top());
			que[id[x]].pop();
			que[id[e[i].to]].pop();
		}
		while (tot) que[id[x]].push(tmp[tot]),tot--;
	}
	if (!id[x]) id[x]=++sz;
	que[id[x]].push(a[x]);
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=2;i<=n;i++)
	{
		int x;scanf("%d",&x);
		addedge(x,i);
	}
	pre(1);
	dfs(1);
	LL ans=0;
	while (!que[id[1]].empty()) ans+=que[id[1]].top(),que[id[1]].pop();
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值