CF600E Lomsat gelral(树上启发式合并)
Posted Keyzee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF600E Lomsat gelral(树上启发式合并)相关的知识,希望对你有一定的参考价值。
题目链接:https://codeforces.com/problemset/problem/600/E
这是一道树上启发式合并的题,就只是在模板的基础上稍微变化了一下
解题思路:我们需要计算当前u节点的答案,要计算加入非重链节点对此答案的影响,在计算加入节点对ans影响的时候,遍历u除了重链外的所有子树节点(因为重链部分的颜色已经被记录在了cnt内),当目前颜色数量(cnt[col[x]])大于记录的最大颜色数,就更新一下最大颜色数和sum颜色编号和,如果等于最大颜色数那么就让sum加等于这个颜色编号。
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<set>
#include<queue>
#define int long long
#define el "\\n"
#define inf 0x3f3f3f3f
using namespace std;
using ll = long long;
const int N = 100005;
const int mod = 9901;
int col[N];
vector<int> G[N];
int dfn[N], ctr[N], tot, siz[N], rnk[N], son[N];
int cnt[N], sum, mx, ans[N];
void add(int x)
cnt[col[x]] ++;
if (cnt[col[x]] > mx)
mx = cnt[col[x]];
sum = col[x];
else if (cnt[col[x]] == mx) sum += col[x];
void del(int x)
cnt[col[x]] --;
int getans()
return sum;
void dfs1(int u, int p)
dfn[u] = ++tot;
siz[u] = 1;
rnk[tot] = u;
for (auto v :G[u])
if (v != p)
dfs1(v, u);
siz[u] += siz[v];
if (siz[son[u]] < siz[v]) son[u] = v;
ctr[u] = tot;
void dfs2(int u, int p, bool keep)
for (auto v :G[u])
if (v != p && v != son[u])
dfs2(v, u, false);
if (son[u]) dfs2(son[u], u, true);
for (auto v :G[u])
if (v != p && v != son[u])
for (int i = dfn[v]; i <= ctr[v]; i++)
add(rnk[i]);
add(u);
ans[u] = getans();
if (!keep)
for (int i = dfn[u]; i <= ctr[u]; i++)
del(rnk[i]);
sum = mx = 0;
signed main()
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> col[i];
for (int i = 1; i < n; i++)
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
dfs1(1, 0);
dfs2(1, 0, 1);
for (int i = 1; i <= n; i++) cout << ans[i] << " ";
return 0;
永远热爱,永远向着光。
CF600E Lomsat gelral(树上启发式合并)
Code:
#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
typedef long long LL;
using namespace std;
const int N = 1e5+10,M=2*N;
int n,m;
int h[N],e[M],ne[M],idx;
void add(int a,int b)
ne[idx]=h[a],e[idx]=b,h[a]=idx++;
int sz[N],son[N];
LL cnt[N],sum,mx;//记录当前节点为根子树的颜色数,最大颜色之和,最大颜色
LL ans[N];
int clr[N];
int dfs_son(int u,int fa)
sz[u]=1;
int pson=0;
for(int i=h[u];~i;i=ne[i])
int k=e[i];
if(k==fa)continue;
dfs_son(k,u);
sz[u]+=sz[k];
if(sz[k]>sz[pson])pson=k;
son[u]=pson;
return sz[u];
void calc(int u,int fa,int pson)//算所有轻儿子的贡献,不算u的重儿子子树
cnt[clr[u]]++;
if(cnt[clr[u]]>mx)mx=cnt[clr[u]],sum=clr[u];
else if(cnt[clr[u]]==mx)sum+=clr[u];
for(int i=h[u];~i;i=ne[i])
int k=e[i];
if(k==fa||k==pson)continue;
calc(k,u,pson);
void remov(int u,int fa)//消除当前子树所有节点贡献
int c=clr[u];
cnt[c]--;
for(int i=h[u];~i;i=ne[i])
int k=e[i];
if(k==fa)continue;
remov(k,u);
void dfs(int u,int fa,int op)//op=1表示重儿子,0表示轻儿子
for(int i=h[u];~i;i=ne[i])
int k=e[i];
if(k==fa||k==son[u])continue;//先跳过重儿子
dfs(k,u,0);
if(son[u])dfs(son[u],u,1);//最后遍历轻儿子
calc(u,fa,son[u]);
ans[u]=sum;
if(!op)remov(u,fa),mx=sum=0;//记得清空
signed main()
cin>>n;
memset(h, -1, sizeof h);
for(int i=1;i<=n;i++)cin>>clr[i];
for(int i=1;i<n;i++)
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
dfs_son(1,-1);
dfs(1,-1,1);
for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
以上是关于CF600E Lomsat gelral(树上启发式合并)的主要内容,如果未能解决你的问题,请参考以下文章
CF 600 E Lomsat gelral —— 树上启发式合并