Dsu on tree

Dsu on tree 代指树上启发式合并,并非是并查集个人觉得这个算法的思想跟莫队有些许相似,但是又利用了树链剖分的一些性质,从而使得复杂度大大降低,优秀的o(nlgn)

需要的前置技能:链式前向星,树链剖分。

U41492 树上数颜色

给出一棵结点有不同颜色的数,询问某个子树有多少种不同的颜色?

传送门
思考一下最暴力的做法,对于每个结点都开一个cnt数组,记录自己的颜色,然后往上合并,处理完以后提供查询,显然空间得炸!

我们可以很显然的看到,一个节点是由它的子节点的合并而来,那么如果我们使用一个数组,去记录某个儿子,然后记录到答案以后再清空,再给下一个儿子使用,这样可以吗?显然是可以的,但是时间复杂度就不允许了?怎么办?
树链剖分的性质:一个点到根路径上不超过log条轻边,等价于一个点最多会被暴力合并logn次

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int cnt, head[N];
struct E
{
    int to, nex;
} e[N << 1];
inline void add_edge(int u, int v)
{
    e[++cnt].to = v, e[cnt].nex = head[u], head[u] = cnt;
}
int siz[N], son[N];
void dfs(int u, int fa)
{
    siz[u] = 1;
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}
int a[N], ok[N], cot[N], sum;
void cal(int u, int fa, int val)
{
    cot[a[u]] += val;
    if(val == 1 && cot[a[u]] == 1) sum++;
    if(val == -1 && cot[a[u]] == 0) sum--;
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v != fa) cal(v, u, val);
    }
}
void dsu(int u, int fa, bool flag)
{
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v != fa && v != son[u]) dsu(v, u, true);
    }
    if(son[u] != 0) dsu(son[u], u, false);
    cot[a[u]]++;
    if(cot[a[u]] == 1) sum++;
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v != fa && v != son[u]) cal(v, u, 1);
    }
    ok[u] = sum;
    if(flag) cal(u, fa, -1);
}
int n, m;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n;
    for(int i = 1, u, v; i < n; ++i)
    {
        cin >> u >> v;
        add_edge(u, v), add_edge(v, u);
    }
    for(int i = 1; i <= n; ++i) cin >> a[i];
    dfs(1, 0);
    dsu(1, 0, 0);
    cin >> m;
    int x;
    while(m-- && cin >> x) cout << ok[x] << "\n";
    return 0;
}

Lomsat gelral(树上众数问题)

传送门
Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 1e5 + 10;
int cnt, head[N];
struct E
{
    int to, nex;
} e[N << 1];
inline void add_edge(int u, int v)
{
    e[++cnt].to = v, e[cnt].nex = head[u], head[u] = cnt;
}
int siz[N], son[N];
void dfs(int u, int fa)
{
    siz[u] = 1;
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}
LL ok_sum, ok[N];
int n, a[N], cot[N], mx;
void cal(int u, int fa, int val)
{
    cot[a[u]] += val;
    if(cot[a[u]] > mx) mx = cot[a[u]], ok_sum = a[u];
    else if(cot[a[u]] == mx)  ok_sum += a[u];
    for(int i = head[u];  i; i = e[i].nex)
    {
        int v =  e[i].to;
        if(v == fa) continue;
        cal(v, u, val);
    }
}
void dsu(int u, int fa, bool flag)
{
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v != fa && v != son[u]) dsu(v, u, 1);
    }
    if(son[u]) dsu(son[u], u, 0);
    for(int i = head[u]; i; i = e[i].nex)
    {
        int v = e[i].to;
        if(v != fa && v != son[u]) cal(v, u, 1);
    }
    cot[a[u]]++;
    if(cot[a[u]] > mx) mx = cot[a[u]], ok_sum = a[u];
    else if(cot[a[u]] == mx)  ok_sum += a[u];
    ok[u] = ok_sum;
    if(flag) cal(u, fa, -1), ok_sum = 0, mx = 0;
}
signed main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for(int i = 1, u, v; i < n; ++i)
    {
        scanf("%d%d", &u, &v);
        add_edge(u, v);
        add_edge(v, u);
    }
    dfs(1, 0);
    dsu(1, 0, 0);
    for(int i = 1; i <= n; ++i) printf("%lld ", ok[i]);
}
posted @ 2022-08-18 08:58  std&ice  阅读(63)  评论(0编辑  收藏  举报