树上启发式合并-附有例题CF600E

树上启发式合并(dsu on tree)

但这个 dsu 应该和并查集没啥关系

总体思想就是利用重链剖分的性质,将小堆(轻儿子)往大堆(重儿子)合并,这样子可以达到 log 的复杂度
以下是几个重要函数

  1. void dfs1(int u, int father){} 得到一系列重要数组,其中包括 son[]
  2. void dfs2(int u, int op){} 计算 u 子树的答案
  3. add_ans(int u, int xson){} 将 u 子树除了 xson 子树的所有加入答案,是合并轻子树的操作
  4. del_ans(int u){} 将 u 子树的答案删除,清空 cnt 数组,用于第一遍 dfs2 后轻子树的删除操作
    第一次敲的时候 add_ans 函数敲成了 add 函数......
int n, m;
ll col[N];

int head[N], cnt;
struct Edge{
    int from, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

int deep[N], fa[N], siz[N], son[N];

void dfs1(int u, int father){
    fa[u] = father;
    deep[u] = deep[father] + 1;
    siz[u] = 1;
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == father) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
    }
}

ll tmpans, ans_cnt, num[N], ans[N];
void add_ans(int u, int xson){
    num[col[u]]++;
    if(num[col[u]] == ans_cnt){
        tmpans += col[u];
    }else if(num[col[u]] > ans_cnt){
        ans_cnt = num[col[u]];
        tmpans = col[u];
    }
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == xson || v == fa[u]) continue;
        add_ans(v, xson);
    }
}
void del_ans(int u){
    num[col[u]]--;
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == fa[u]) continue;
        del_ans(v);
    }
}

void dfs2(int u, int op){
    for(int i = head[u]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v, 0);
    }
    if(son[u] != 0){
        dfs2(son[u], 1);
    }
    add_ans(u, son[u]);
    ans[u] = tmpans;
    if(op == 0){
        tmpans = ans_cnt = 0;
        del_ans(u);
    }
}

再补一个 main 函数

点击查看代码
void solve() {
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> col[i];
    }
    for(int i = 1; i < n; i++){
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    for(int i = 1; i <= n; i++){
        cout << ans[i] << " ";
    }
}

Lomsat gelral

题面翻译

  • 有一棵 n 个结点的以 1 号结点为根的有根树
  • 每个结点都有一个颜色,颜色是以编号表示的, i 号结点的颜色编号为 ci
  • 如果一种颜色在以 x 为根的子树内出现次数最多,称其在以 x 为根的子树中占主导地位。显然,同一子树中可能有多种颜色占主导地位。
  • 你的任务是对于每一个 i[1,n],求出以 i 为根的子树中,占主导地位的颜色的编号和。
  • n105,cin

题目描述

You are given a rooted tree with root in vertex 1 . Each vertex is coloured in some colour.

Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the subtree of vertex v more times than colour c . So it's possible that two or more colours will be dominating in the subtree of some vertex.

The subtree of vertex v is the vertex v and all other vertices that contains vertex v in each path to the root.

For each vertex v find the sum of all dominating colours in the subtree of vertex v .

输入格式

The first line contains integer n ( 1<=n<=105 ) — the number of vertices in the tree.

The second line contains n integers ci ( 1<=ci<=n ), ci — the colour of the i -th vertex.

Each of the next n1 lines contains two integers xj,yj ( 1<=xj,yj<=n ) — the edge of the tree. The first vertex is the root of the tree.

输出格式

Print n integers — the sums of dominating colours for each vertex.

样例 #1

样例输入 #1

4
1 2 3 4
1 2
2 3
2 4

样例输出 #1

10 9 3 4

样例 #2

样例输入 #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

posted @   9102700  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
  1. 1 鼓楼 赵雷
  2. 2 我们的歌 王力宏
  3. 3 老街 李荣浩
  4. 4 周杰伦
  5. 5 可惜没如果 林俊杰
  6. 6 不将就 李荣浩
  7. 7 南方姑娘 赵雷
  8. 8 南方姑娘(弹唱版) 赵雷
  9. 9 如果可以 韦礼安
  10. 10 写给黄淮 邵帅
  11. 11 我想念 汪苏泷
  12. 12 雨天 汪苏泷
  13. 13 雨天雨天 汪苏泷
  14. 14 成都 赵雷
我们的歌 - 王力宏
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 王力宏/陈信延

作曲 : 王力宏

编曲 : 王力宏

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

情人总分分合合

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

电吉他/其他乐器:王力宏

鼓手:Eric Fawcrtt

贝斯:John Mumson

录音师/录音室:王力宏/Homeboy Music Studios,Taipei

OP:HIM Music Publishing Inc.

OP:Homeboy Music,Inc,Taiwan

SP:Sony Music Publishing(Pre)Ltd.Taiwan Branch

点击右上角即可分享
微信分享提示