洛谷5290 十二省联考2019 春节十二响 贪心 multiset/堆 启发式合并

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/89201988

题目链接

题意:
给你一棵 n n 个点的有根树,根是 1 1 号节点,每个节点有一个权值,你要把所有点划分成若干个集合,每个集合中的点在树上不能有父子关系,每个集合的权值是所有集合中的点权值最大的那个。问所有集合的最小权值和是多少。 n < = 2 e 5 n<=2e5

题解:
送我退役的第三道题。

感觉除了一些复杂度完全没法优化的暴力之外,基本都是要基于一些贪心的思想来做。我现在知道两种做法,一种是正解的思路,一种是他们当场想出的一种思路。

先说说正解的思路。正解的关键是考虑从下向上合并子树的过程答案的变化。我们发现,对于两棵子树,我们都把子树中的点从大到小排好序之后,全局最大值一定会累加进答案,那么为了让总答案最小,我们一定对尽可能的消去另一个子树中最大的那个点。这样我们维护一个子树内的权值和,然后每次合并子树的时候,从大到小拿出每一个点,然后对应合并,保留权值大的那个,对应位置权值小的那个就和权值大的那个可以放在一个集合,不会统计到答案中了。我们发现维护每个子树的大小顺序可以用一个堆或者multiset来做,在合并的时候要启发式合并,这里要好好思考一下写法,以免TLE或者MLE,我现在堆的写法还有点问题,于是代码先用一个multiset的写法了。当然用左偏树写是可以一个 l o g log 的。

再说一下第二种思路。第二种思路是,我们每次先找出全局最大的数,这个数成为这一次的答案,然后它子树内的点和它到根的路径上的点都不能选了。然后我们每次贪心地在其他可以选的点中选权值最大的,直到整个子树没有可以选的点,那么刚才的那些点就成为一个集合。我们用树剖来维护之前的过程,大体上要维护一个区间打不可以被选的标记、整体删除标记、单点修改、区间最大值之类的操作。具体细节我也没太仔细想,但是考场是有人用这个写法过掉了的。这样每个点是只会被拿出来一次的,于是复杂度是对的,是两个 l o g log

这题数据不是很卡,反正据我所知各种一个 l o g log 和两个 l o g log 的写法都能过。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,val[200010],hed[200010],cnt,fa[200010],QAQ[200010],shu,book[200010];
struct node
{
	int to,next;
}a[400010];
multiset<int> s[200010],ji;
long long res[200010];
inline int read()
{
	int x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x; 
}
inline void add(int from,int to)
{
	a[++cnt].to=to;
	a[cnt].next=hed[from];
	hed[from]=cnt;
}
inline void dfs(int x)
{
	int pd=0;
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		dfs(y);		
		res[x]+=res[y];
		if(!pd)
		{
			pd=1;
			book[x]=book[y];
			continue;
		}
		if(s[book[x]].size()<s[book[y]].size())
		swap(s[book[x]],s[book[y]]);
		while(!s[book[y]].empty())
		{
			int u=*(--s[book[x]].end()),v=*(--s[book[y]].end());
			res[x]-=min(u,v);
			ji.insert(max(u,v));
			s[book[x]].erase(s[book[x]].find(u));
			s[book[y]].erase(s[book[y]].find(v));
		}
		while(!ji.empty())
		{
			int qwq=*(--ji.end());
			s[book[x]].insert(qwq);
			ji.erase(ji.find(qwq));
		}
		s[book[y]].clear();
	}
	if(!book[x])
	book[x]=x;
	s[book[x]].insert(val[x]);
}
int main()
{
	n=read();
	for(int i=1;i<=n;++i)
	{
		val[i]=read();
		res[i]=val[i];
	}
	for(int i=2;i<=n;++i)
	{
		fa[i]=read();
		add(fa[i],i);
	}
	dfs(1);
	printf("%lld\n",res[1]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/89201988
今日推荐