dsu on tree简介及例题


d s u   o n   t r e e dsu ~on ~tree dsu on tree
树上启发式合并,多用于对子树的暴力询问,通过使用轻重链定义来进行优化,将算法复杂度降到 O ( n l o g n ) O(nlogn) O(nlogn)
算是一种优雅的暴力


先用一道dsu on tree比较模版的题来引一下
codeforces 600E
题意:

一棵树n个点,每个点有一个颜色 要求每个结点子树的出现哪个颜色次数最多 如果有多个颜色次数同时最多,结果为这些颜色编号相加

首先可以考虑暴力的写法,对每个结点,暴力统计他的子树每个颜色有多少,如果出现超过答案的进行更新,等于答案的进行相加
这样算下来复杂度应该是 O ( n 2 ) O(n^2) O(n2)
那么这个时候,就可以考虑用dsu on tree进行优化


首先需要知道的定义是重儿子和轻儿子
对于一个结点,他某个儿子的这个子树所包含的结点最多,那么这个儿子就是重儿子,其他的儿子就是轻儿子


原理

引入一个结论:从某一个点到根的路径中轻链的个数不会超过 l o g n logn logn

证明:
从一个结点开始往上到根的过程中,每次交汇到一个点,如果这个点是轻链,那么这个点的子节点数一定比重链的少,假设这个轻链对应子树结点大小为 y y y,这个交汇点子树结点大小为 x x x,那么 y < x / 2 y<x/2 y<x/2
如果这个点到根上有z个轻链,那么需要交汇z次
那么这个点的子树结点个数会成为 n u m < = n 2 z num<=\frac{n}{2^z} num<=2zn
n u m > = 1 num>=1 num>=1,所以 z < l o g n z<logn z<logn

并且,由于递归的性质,每次在递归return之后会回到一个父亲,那么在返回的时候,我们之前算的子树完全可以保留一个回到父亲节省下次计算,那么,我们每次最后访问重儿子,并且把重儿子的贡献保留传递给父亲继续计算,然后把轻儿子暴力计算,通过这个结论我们可以发现这样的复杂度最后只会达到 O ( n l o g n ) O(nlogn) O(nlogn)


算法

1.通过dfs计算出重儿子,轻儿子使用
2.先dfs轻儿子,然后dfs重儿子
3.计算贡献,如果当前儿子是一个重儿子,那么保留这个贡献,否则将贡献清除

可以简化为如下的模版:

int sz[N], son[N];
vector<int> g[N];
void dfs(int u, int fa) {
   
    sz[u] = 1;
    for (auto v : g[u]) {
   
        if (v == fa) continue;
        dfs(v, u);
        sz[u] += sz[v];
        if (sz[v] > sz[son[u]]) son[u] = v;
    }
}
int Son;
//add函数为计算贡献的方法
void add(int u, int fa, int val) {
   
    
}
void dfs1(int u, int fa, int op) {
   
    for (auto v : g[u]) {
   
        if (v == fa || v == son[u]) continue;
        dfs1(v, u, 0);
    }
    if (son[u]) dfs1(son[u], u, 1), Son = son[u];
    add(u, fa, 1)
    Son = 0;
    if (!op) add(u, fa, -1);
}

这样就形成了一个模版


例题

1. Lomsat gelral(引题)

首先先看刚才的引题,只需要修改这个add函数即可
对每个暴力的点加入他的颜色,用一个cnt函数计算每个颜色的贡献
并且看这个贡献是否达到或超过最大值,进行颜色答案的计算即可
AC代码

/*
    Author : zzugzx
    Lang : C++
    Blog : blog.csdn.net/qq_43756519
*/

#include<bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
#define mem(a, b) memset(a, b, sizeof(a))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod = 1e9 + 7;
const int mod = 998244353;

const double eps = 1e-8;
const double pi = acos(-1.0);
const int maxn = 1e6 + 10;
const int N = 1e2 + 10;
const ll inf = 0x3f3f3f3f;
const ll oo = 8e18;
const int dir[][2
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值