P4556 [Vani有约会] 树上差分 + 线段树合并

该博客介绍了如何使用树上点差分和线段树合并算法来高效地解决给定路径上物品计数的问题。在O(MlogN)的时间复杂度内,通过在每条路径上更新节点,并利用线段树维护每个房屋内数量最多的物品类型。算法首先进行朴素的路径计数,然后利用树的性质进行优化,最后进行深度优先搜索以合并节点并找到答案。
摘要由CSDN通过智能技术生成
题意

传送门 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

题解

朴素的对放物品的路径上每座房屋计数,时空复杂度 O ( M N ) O(MN) O(MN)。考虑树上点差分,对于路径 ( x , y ) (x,y) (x,y),在 x , y x,y x,y z z z 类型物品增 1 1 1,在 l c a ( x , y ) , f a t h e r ( l c a ( x , y ) ) lca(x,y),father(lca(x,y)) lca(x,y),father(lca(x,y)) z z z 类型物品减 1 1 1

为了高效地求解各个房屋内数量最多的物品类型,使用线段树合并算法,对每个房屋代表的节点建一颗动态开点的权值线段树,处理完各条路径后,进行一次 D F S DFS DFS,将所有子节点与当前节点合并,同时记录答案。时空复杂度 O ( M log ⁡ N ) O(M\log N) O(MlogN)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005, maxlg = 17;
struct node
{
#define lc(k) tree[k].lc
#define rc(k) tree[k].rc
#define n(k) tree[k].n
#define x(k) tree[k].x
    int lc, rc, n, x;
} tree[4 * maxn * maxlg];
int N, M, tot, rt[maxn], res[maxn];
int lg[maxn], dep[maxn], fa[maxlg][maxn];
int E, head[maxn], to[maxn << 1], nxt[maxn << 1];

void add(int x, int y) { to[++E] = y, nxt[E] = head[x], head[x] = E; }

void dfs(int x, int f, int d)
{
    fa[0][x] = f, dep[x] = d;
    for (int i = 1; i <= lg[d]; ++i)
        fa[i][x] = fa[i - 1][fa[i - 1][x]];
    for (int i = head[x]; i; i = nxt[i])
    {
        int y = to[i];
        if (y != f)
            dfs(y, x, d + 1);
    }
}

int lca(int x, int y)
{
    if (dep[x] < dep[y])
        swap(x, y);
    while (dep[x] > dep[y])
        x = fa[lg[dep[x] - dep[y]]][x];
    if (x == y)
        return x;
    for (int i = lg[dep[x]]; i >= 0;)
        if (fa[i][x] != fa[i][y])
            x = fa[i][x], y = fa[i][y], i = lg[dep[x]];
        else
            --i;
    return fa[0][x];
}

void insert(int &k, int l, int r, int x, int d)
{
    if (!k)
        k = ++tot;
    if (r - l == 1)
    {
        n(k) += d, x(k) = n(k) ? l : 0;
        return;
    }
    int m = (l + r) >> 1;
    x < m ? insert(lc(k), l, m, x, d) : insert(rc(k), m, r, x, d);
    int p = n(lc(k)) >= n(rc(k)) ? lc(k) : rc(k);
    n(k) = n(p), x(k) = x(p);
}

int merge(int p, int q, int l, int r)
{
    if (!p)
        return q;
    if (!q)
        return p;
    if (r - l == 1)
    {
        n(p) += n(q), x(p) = n(p) ? l : 0;
        return p;
    }
    int m = (l + r) >> 1;
    lc(p) = merge(lc(p), lc(q), l, m), rc(p) = merge(rc(p), rc(q), m, r);
    int k = n(lc(p)) >= n(rc(p)) ? lc(p) : rc(p);
    n(p) = n(k), x(p) = x(k);
    return p;
}

void gdfs(int x, int f)
{
    for (int i = head[x]; i; i = nxt[i])
    {
        int y = to[i];
        if (y != f)
        {
            gdfs(y, x);
            rt[x] = merge(rt[x], rt[y], 1, maxn);
        }
    }
    res[x] = x(rt[x]);
}

int main()
{
    scanf("%d%d", &N, &M);
    for (int i = 1, a, b; i < N; ++i)
    {
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    lg[0] = -1;
    for (int i = 1; i < maxn; ++i)
        lg[i] = lg[i - 1] + (1 << (lg[i - 1] + 1) == i);
    dfs(1, 0, 0);
    for (int i = 1; i <= N; ++i)
        rt[i] = ++tot;
    for (int i = 1, x, y, z; i <= M; ++i)
    {
        scanf("%d%d%d", &x, &y, &z);
        int p = lca(x, y);
        insert(rt[x], 1, maxn, z, 1);
        insert(rt[y], 1, maxn, z, 1);
        insert(rt[p], 1, maxn, z, -1);
        if (fa[0][p])
            insert(rt[fa[0][p]], 1, maxn, z, -1);
    }
    gdfs(1, 0);
    for (int i = 1; i <= N; ++i)
        printf("%d\n", res[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值