树上启发式合并与dsu on tree

题目链接:https://codeforc.es/contest/600/problem/E

题目大意:给一颗以1为根的树,共n个点。点有颜色,颜色从1-n编号。问每颗子树中出现次数最多的颜色 的编号之和。

启发式合并做法:

#include
#include
#include
#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;
vectornxt[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]]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<

dsu on tree 做法:

#include 
#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;
vectornxt[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]]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<

 

你可能感兴趣的:(算法模板,树论)