P4556 [Vani有约会]雨天的尾巴 线段树合并做法*

22 篇文章 0 订阅
5 篇文章 0 订阅

Link
线段树合并的板子题,等刷过几道再去补树剖做法。
注意: 线段树空间开很多倍的同时,不要忘记把root数组也同样开到很多倍

思路

其实就是树上差分+LCA,但空间复杂度 O ( N M ) O(NM) O(NM),通过线段树合并将空间复杂度降至 O ( ( N + M ) log ⁡ ( N + M ) ) O((N+M)\log(N+M)) O((N+M)log(N+M))
具体来说,对树上每个点建立一棵动态开点的权值线段树,支持修改某个位置,维护区间最大值和最大值所在的位置。

代码

const int maxn = 4e5 + 10;
const int maxm = 4e5 + 10;
struct SegmentTree {
    int lc, rc;
    int dat;
#define lc(p) tree[p].lc
#define rc(p) tree[p].rc
#define dat(p) tree[p].dat
} tree[maxn << 5];
int rt[maxn << 5];
int tot;
int x[maxn], y[maxn], z[maxn];
void push(int p) {
    if(lc(p) == 0) {
        dat(p) = dat(rc(p));
        rt[p] = rt[rc(p)];
        return;
    }
    if(rc(p) == 0) {
        dat(p) = dat(lc(p));
        rt[p] = rt[lc(p)];
        return;
    }
    if(dat(lc(p)) >= dat(rc(p))) {
        dat(p) = dat(lc(p));
        rt[p] = rt[lc(p)];
    }
    else {
        dat(p) = dat(rc(p));
        rt[p] = rt[rc(p)];
    }
}
int insert(int p, int l, int r, int pos, int d) {
    if(!p) p = ++tot;
    if(l == r) {
        dat(p) += d;
        rt[p] = pos;
        return p;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)
        lc(p) = insert(lc(p), l, mid, pos, d);
    else
        rc(p) = insert(rc(p), mid + 1, r, pos, d);
    push(p);
    return p;
}
int merge(int p, int q, int l, int r) {
    if(!p) return q;
    if(!q) return p;
    if(l == r) {
        dat(p) += dat(q);
        return p;
    }
    int mid = (l + r) >> 1;
    lc(p) = merge(lc(p), lc(q), l, mid);
    rc(p) = merge(rc(p), rc(q), mid + 1, r);
    push(p);
    return p;
}
int n;
int Log2[maxn], fa[maxn][30], dep[maxn];
bool vis[maxn];
int head[maxn];
int p;
struct Edge {
    int to, dis = 1, next;
}edge[maxm];
void dfs(int cur = 1, int fath = 0) {
    if(vis[cur]) return;
    vis[cur] = true;
    dep[cur] = dep[fath] + 1;
    fa[cur][0] = fath;
    for(int i = 1; i <= Log2[dep[cur]]; i++)
        fa[cur][i] = fa[fa[cur][i-1]][i-1];
    for(int i = head[cur]; i; i = edge[i].next)
        dfs(edge[i].to, cur);
}
int LCA(int a, int b) {
    if(dep[a] > dep[b])
        swap(a, b);
    while(dep[a] != dep[b])
        b = fa[b][Log2[dep[b]-dep[a]]];
    if(a == b)
        return a;
    for(int k = Log2[dep[a]]; k >= 0; k--)  //跳跃长度从长到短
        if(fa[a][k] != fa[b][k]) {
            a = fa[a][k];
            b = fa[b][k];
        }
    return fa[a][0];
}
void init() {
    for(int i = 1; i <= n; i++) {
        dep[i] = 0;
        head[i] = 0;
    }
    p = 0;
    for(int i = 2; i <= n; i++)
        Log2[i] = Log2[i / 2] + 1;
}
void add_edge(int u, int v, int w = 1) {
    p++;
    edge[p].to = v;
    edge[p].dis = w;
    edge[p].next = head[u];
    head[u] = p;
}
int r = 0;
int ans[maxn];
void calc(int u, int fa) {
    for(int i = head[u]; i; i = edge[i].next) {
        int v = edge[i].to;
        if(v == fa) continue;
        calc(v, u);
        rt[u] = merge(rt[u], rt[v], 1, r);
    }
    ans[u] = rt[rt[u]];
    if(dat(rt[u]) == 0) ans[u] = 0;
}
void solve() {
    tot = 0;
    int m;
    cin >> n >> m;
    init();
    for(int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        add_edge(u, v);
        add_edge(v, u);
    }
    dfs();
    for(int i = 1; i <= m; i++) {
        cin >> x[i] >> y[i] >> z[i];
        r = max(r, z[i]);
    }
    for(int i = 1; i <= m; i++) {
        int lca = LCA(x[i], y[i]);
        rt[x[i]] = insert(rt[x[i]], 1, r, z[i], 1);
        rt[y[i]] = insert(rt[y[i]], 1, r, z[i], 1);
        rt[lca] = insert(rt[lca], 1, r, z[i], -1);
        if(fa[lca][0])
            rt[fa[lca][0]] = insert(rt[fa[lca][0]], 1, r, z[i], -1);
    }
    calc(1, 0);
    for(int i = 1; i <= n; i++)
        cout << ans[i] << endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值