Codeforces 600E Lomsat gelral 树上启发式合并

本文介绍了一种利用启发式合并技巧优化深度优先搜索(DFS)算法的方法,以解决特定类型的树形结构问题:给定一棵包含n个节点的树,每个节点具有不同的颜色,目标是计算每个节点子树中颜色出现次数最多的颜色之和。文章详细阐述了启发式合并的思想,即通过确保每次合并后集合规模至少翻倍的方式减少合并次数,并提供了具体的C++实现代码。

点击打开链接

题意:n个结点的树,每个结点颜色为c[i].n<=1e5,求每个结点的子树中颜色出现次数最多的颜色之和.

启发式合并:一个元素从一个集合被加入另一个集合时, 所在集合的规模至少扩大一倍. 如果没有分离操作且元素数目有限, 合并的次数是logn的.

设两个map<int,ll>:col[u][x] 子树u中颜色x的出现次数,sum[u][x] 子树u中出现次数为x的颜色之和
dfs时,根据u的子树v来更新col[u][x],暴力更新O(n^2)


size小的合并到大的 每个元素合并时 新的map size至少为原来两倍,所以每个元素最多合并logn次 合并次数为nlogn 每次合并log 时间复杂度O(n(logn)^2)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const ll mod=1e9;
const ll inf=1e18;
const int N=2e5+20;
ll n,c[N],ans[N];
map<int,int> col[N];//col[u][x] ×ÓÊ÷uÖÐÑÕÉ«xµÄ³öÏÖ´ÎÊý 
map<int,ll> sum[N];//sum[u]
vector<int> e[N];
void merge(int u,int v)
{
	if(col[u].size()<col[v].size())//
		swap(col[u],col[v]),swap(sum[u],sum[v]);
	for(map<int,int>::iterator it=col[v].begin();it!=col[v].end();it++)
	{
		sum[u][col[u][it->first]]-=it->first;
		col[u][it->first]+=col[v][it->first];		
		sum[u][col[u][it->first]]+=it->first;
	}
}
void dfs(int u,int fa)
{
	col[u][c[u]]=1;
	sum[u][1]=c[u];
	for(int i=0;i<e[u].size();i++)
	{
		int v=e[u][i];
		if(v==fa)	continue;
		dfs(v,u);
		merge(u,v);
	}	
	ans[u]=sum[u].rbegin()->second;
}
int main()
{
	while(cin>>n)
	{
		int u,v;
		for(int i=1;i<=n;i++)
			scanf("%d",&c[i]),e[i].clear();
		for(int i=1;i<=n-1;i++)
		{
			scanf("%d%d",&u,&v);
			e[u].push_back(v);
			e[v].push_back(u);
		}	
		dfs(1,-1);	
		for(int i=1;i<=n;i++)
			printf("%I64d%c",ans[i],i==n?'\n':' ');	
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值