题意: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;
}

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

2836

被折叠的 条评论
为什么被折叠?



