题目链接:https://codeforc.es/contest/600/problem/E
题目大意:给一颗以1为根的树,共n个点。点有颜色,颜色从1-n编号。问每颗子树中出现次数最多的颜色 的编号之和。
启发式合并做法:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define ll long long
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define pb push_back
using namespace std;
using namespace __gnu_pbds;
const int N = 1e5+1000;
vector<int>nxt[N];
int siz[N],son[N];
void split(int u,int f) {
son[u] = 0;
siz[u] = 1;
for(auto v:nxt[u]) {
if(v==f)continue;
split(v,u);
siz[u] += siz[v];
if(siz[son[u]]<siz[v]) son[u] = v;
}
}
//----------
ll val[N],ans[N],p[N];
unordered_map<ll,ll>num[N],cnt[N];
void add(int u,int f,int t) {
int col = val[u];
cnt[t][num[t][col]] -= col;
num[t][col]++;
cnt[t][num[t][col]] += col;
if(num[t][col]>p[t]) p[t]++;
for(auto v:nxt[u]) {
if(v==f) continue;
add(v,u,t);
}
}
void dfs(int u,int f) {
for(auto v:nxt[u]) {
if(v==f) continue;
dfs(v,u);
}
if(son[u]) { //继承重儿子的信息
swap(num[u],num[son[u]]);
swap(cnt[u],cnt[son[u]]);
swap(p[u],p[son[u]]);
}
for(auto v:nxt[u]) {
if(v==f||v==son[u]) continue;
add(v,u,u); //暴力合并轻儿子
}
int col = val[u]; //和并这个点本身
cnt[u][num[u][col]] -= col;
num[u][col]++;
cnt[u][num[u][col]] += col;
if(num[u][col]>p[u]) p[u]++;
ans[u] = cnt[u][p[u]];
}
int main() {
//ios::sync_with_stdio(0);
int n;
cin>>n;
rep(i, 1, n) cin>>val[i];
rep(i, 1, n-1) {
int u,v;
cin>>u>>v;
nxt[u].pb(v);
nxt[v].pb(u);
}
split(1,0);
dfs(1,0);
rep(i, 1, n) cout<<ans[i]<<' ';
return 0;
}
dsu on tree 做法:
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define mp make_pair
#define pb push_back
#define ll long long
using namespace std;
const int N = 1e5+1000;
vector<int>nxt[N];
int siz[N],son[N];
void split(int u,int f) {
son[u] = 0;
siz[u] = 1;
for(auto v:nxt[u]) {
if(v==f)continue;
split(v,u);
siz[u] += siz[v];
if(siz[son[u]]<siz[v]) son[u] = v;
}
}
//---------------------
ll p,num[N],ans[N],Son,cnt[N],val[N];
void add(int u,int f,int data) {
cnt[num[val[u]]] -= val[u];
num[val[u]] += data;
cnt[num[val[u]]] += val[u];
if(num[val[u]]>p) p++;
for(auto v:nxt[u]) {
if(v==f||v==Son) continue;
add(v,u,data);
}
}
void dfs(int u,int f,int opt) {
for(auto v:nxt[u]) {
if(v==f||v==son[u]) continue;
dfs(v,u,0); //计算轻儿子的答案,不保留贡献
}
if(son[u]) dfs(son[u],u,1),Son = son[u]; //计算重儿子的答案,保留贡献
add(u,f,1),Son = 0; //遍历所有轻儿子,将贡献合并
ans[u] = cnt[p]; //记录答案
if(!opt) add(u,f,-1),p = 0; //如果u点是轻儿子,消除全部影响,清空所有信息
}
int main() {
//freopen("a.txt","r",stdin);
ios::sync_with_stdio(0);
int n;
cin>>n;
rep(i, 1, n) cin>>val[i];
rep(i, 1, n-1) {
int u,v;
cin>>u>>v;
nxt[u].pb(v);
nxt[v].pb(u);
}
split(1,0);
dfs(1,0,0);
rep(i, 1, n) cout<<ans[i]<<' ';
return 0;
}