3189. Lomsat gelral
题目链接
3189. Lomsat gelral
给定一棵由 \(n\) 个节点组成的树。
树的节点编号为 \(1 \sim n\),其中 \(1\) 号节点为树的根节点。
每个节点上都标有某种颜色。
第 \(i\) 个节点上的颜色为 \(c_i\)。
如果在以节点 \(v\) 为根节点的子树中,没有任何颜色的出现次数超过颜色 \(c\) 的出现次数,那么我们称颜色 \(c\) 为该子树的主要颜色。
一些子树的主要颜色可能不止一种。
以节点 \(v\) 为根节点的子树由节点 \(v\) 以及到达根节点的路径中包含节点 \(v\) 的其它所有节点共同组成。
对于每个节点 \(v(1 \le v \le n)\),请你求出以该节点为根节点的子树的所有主要颜色之和。
输入格式
第一行包含整数 \(n\)。
第二行包含 \(n\) 个整数 \(c_i\),表示每个节点的颜色编号。
接下来 \(n-1\) 行,每行包含两个整数 \(x,y\) 表示节点 \(x\) 和 \(y\) 之间存在一条边。
注意,节点 \(1\) 为树的根节点。
输出格式
共一行,输出 \(n\) 个整数,其中第 \(i\) 个整数表示以节点 \(i\) 为根节点的子树的所有主要颜色之和。
数据范围
\(1 \le n \le 10^5\),
\(1 \le c_i \le n\),
\(1 \le x,y \le n\)
输入样例1:
4
1 2 3 4
1 2
2 3
2 4
输出样例1:
10 9 3 4
输入样例2:
15
1 2 3 1 2 3 3 1 1 3 2 2 1 2 3
1 2
1 3
1 4
1 14
1 15
2 5
2 6
2 7
3 8
3 9
3 10
4 11
4 12
4 13
输出样例2:
6 5 4 3 2 3 3 1 1 3 2 2 1 2 3
解题思路
启发式合并,树上启发式合并
由下而上计算答案,对于一个根节点来说每次计算一棵子树都需要清零防止对相邻子树产生影响,而计算最后一棵子树时不用清零,直接回溯到根节点再统计根节点的贡献即可,\(\color{red}{所以如何选择这最后一棵子树?}\)直觉上应该选择重儿子所在的子树。具体说,先处理轻儿子所在的子树,同时每处理一棵轻儿子所在的子树后清零计数器,最后处理重儿子后不清零,再计数那些轻儿子计算当前根节点贡献,关键在于 \(dfs\) 的写法
复杂度证明:对于每个节点,其被计算的规模取决于上面轻边的数量,而如果遇上一条轻边,其子树大小至少变为原来的 \(2\) 倍,故最多有 \(O(logn)\) 条轻边,故:
- 时间复杂度:\(O(nlogn)\)
代码
// Problem: Lomsat gelral
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/3191/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#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;
}