题目链接
给定一棵由 n 个节点组成的树。
树的节点编号为 1∼n,其中 1 号节点为树的根节点。
每个节点上都标有某种颜色。
第 i 个节点上的颜色为 ci。
如果在以节点 v 为根节点的子树中,没有任何颜色的出现次数超过颜色 c 的出现次数,那么我们称颜色 c 为该子树的主要颜色。
一些子树的主要颜色可能不止一种。
以节点 v 为根节点的子树由节点 v 以及到达根节点的路径中包含节点 v 的其它所有节点共同组成。
对于每个节点 v(1≤v≤n),请你求出以该节点为根节点的子树的所有主要颜色之和。
输入格式
第一行包含整数 n。
第二行包含 n 个整数 ci,表示每个节点的颜色编号。
接下来 n−1 行,每行包含两个整数 x,y 表示节点 x 和 y 之间存在一条边。
注意,节点 1 为树的根节点。
输出格式
共一行,输出 n 个整数,其中第 i 个整数表示以节点 i 为根节点的子树的所有主要颜色之和。
数据范围
1≤n≤105,
1≤ci≤n,
1≤x,y≤n
输入样例1:
输出样例1:
输入样例2:
输出样例2:
解题思路
启发式合并,树上启发式合并
由下而上计算答案,对于一个根节点来说每次计算一棵子树都需要清零防止对相邻子树产生影响,而计算最后一棵子树时不用清零,直接回溯到根节点再统计根节点的贡献即可,所以如何选择这最后一棵子树?直觉上应该选择重儿子所在的子树。具体说,先处理轻儿子所在的子树,同时每处理一棵轻儿子所在的子树后清零计数器,最后处理重儿子后不清零,再计数那些轻儿子计算当前根节点贡献,关键在于 dfs 的写法
复杂度证明:对于每个节点,其被计算的规模取决于上面轻边的数量,而如果遇上一条轻边,其子树大小至少变为原来的 2 倍,故最多有 O(logn) 条轻边,故:
代码
#include <bits/stdc++.h>
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,color[N],son[N],sz[N],cnt[N];
LL res[N],sum;
int mx;
vector<int> adj[N];
int dfs_son(int x,int fa)
{
sz[x]=1;
for(int y:adj[x])
{
if(y==fa)continue;
sz[x]+=dfs_son(y,x);
if(sz[son[x]]<sz[y])son[x]=y;
}
return sz[x];
}
void update(int x,int fa,int sign,int s)
{
int c=color[x];
cnt[c]+=sign;
if(mx<cnt[c])mx=cnt[c],sum=c;
else if(cnt[c]==mx)sum+=c;
for(int y:adj[x])
{
if(y==fa||y==s)continue;
update(y,x,sign,0);
}
}
void dfs(int x,int fa,int op)
{
for(int y:adj[x])
{
if(y==fa||y==son[x])continue;
dfs(y,x,0);
}
if(son[x])dfs(son[x],x,1);
update(x,fa,1,son[x]);
res[x]=sum;
if(!op)update(x,fa,-1,0),sum=mx=0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&color[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
adj[x].pb(y),adj[y].pb(x);
}
dfs_son(1,0);
dfs(1,0,1);
for(int i=1;i<=n;i++)printf("%lld ",res[i]);
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!