树上启发式合并与dsu on tree

题目链接: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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值