线段树合并复习笔记

线段树合并和主席树不同的是。主席树的节点还要依赖之前的树结构。而线段树合并是若干个分离的线段树合并起来,在创建节点的时候有些不同。

bzoj2212

按照dfs的顺序处理。显然一个子树内的交换只会影响该子树,所以可以贪心得对于每个子树决定是否交换。算到叶子节点的时候新建一颗 l o g log log个节点的权值线段树。合并左右儿子的时候,实际上是合并左右孩子的两颗线段树。不交换的话就是前面的大权值区间的个数乘以后面的小权值区间的个数;交换的话就反过来。两者取个min就是子树内的答案了。

空间问题:叶子节点的空间是 O ( n l o g n ) O(nlogn) O(nlogn)个节点的。每次合并的时候要新建线段树。最多向上合并 O ( n ) O(n) O(n)次。大致空间开 2 n l o g n 2nlogn 2nlogn应该就ok了。

时间复杂度是均摊 O ( n l o g n ) O(nlogn) O(nlogn)的。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
struct poi {
    int ls, rs, sum;
}tr[N*40];
int n, tot;
LL ans, res1, res2;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
inline void pushup(int k) {
    tr[k].sum = tr[tr[k].ls].sum + tr[tr[k].rs].sum;
}
inline int insert(int l, int r, int x) {
    int p = ++tot;
    if (l == r) {tr[p].sum = 1; return p;}
    int mid = (l + r) / 2;
    if (x <= mid) tr[p].ls = insert(l, mid, x);
    else tr[p].rs = insert(mid + 1, r, x);
    pushup(p);
    return p;
}
inline int merge(int x, int y) {
    if (!x || !y) return x + y;
    int p = ++tot;
    res1 += 1LL * tr[tr[x].ls].sum * tr[tr[y].rs].sum;
    res2 += 1LL * tr[tr[x].rs].sum * tr[tr[y].ls].sum;
    tr[p].ls = merge(tr[x].ls, tr[y].ls);
    tr[p].rs = merge(tr[x].rs, tr[y].rs);
    pushup(p);
    return p;
}
inline int init() {
    int x, p = 0; x = read();
    if (x) p = insert(1, n, x);
    else {
        p = merge(init(), init());
        ans += min(res1, res2);
        res1 = res2 = 0;
    }
    return p;
}
int main() {
    n = read();
    init();
    printf("%lld\n", ans);
    return 0;
}

luoguP3224

每个联通块维护一个权值线段树。 查询就线段树上二分,索引回去就好了。 合并就线段树合并。

#include <cstdio>
const int N = 1e5 + 5;
struct poi {
    int ls, rs, sum;
}tr[N*40];
int fa[N], rt[N], pos[N], n, m, tot;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
inline int insert(int l, int r, int v) {
    int p = ++tot;
    tr[p].sum++; if (l == r) return p;
    int mid = (l + r) / 2;
    if (v <= mid) tr[p].ls = insert(l, mid, v);
    else tr[p].rs = insert(mid + 1, r, v);
    return p;
}
inline int merge(int x, int y) {
    if (!x || !y) return x + y;
    int p = ++tot;
    tr[p].sum = tr[x].sum + tr[y].sum;
    tr[p].ls = merge(tr[x].ls, tr[y].ls);
    tr[p].rs = merge(tr[x].rs, tr[y].rs);
    return p;
}
inline int getf(int x) {return fa[x] == x ? x : fa[x] = getf(fa[x]);}
inline void build(int x, int y) {
    int f1 = getf(x), f2 = getf(y);
    if (f1 != f2) {
        fa[f2] = f1;
        rt[f1] = merge(rt[f1], rt[f2]);
    }
}
inline int query(int k, int l, int r, int v) {
    if (tr[k].sum < v) return -1;
    if (l == r) return l;
    int mid = (l + r) / 2;
    if (tr[tr[k].ls].sum >= v) return query(tr[k].ls, l, mid, v);
    else return query(tr[k].rs, mid + 1, r, v - tr[tr[k].ls].sum);
}
inline char readc() {
    char ch = getchar();
    while (ch != 'B' && ch != 'Q') ch = getchar();
    return ch;
}
int main() {
    n = read(); m = read();
    for (int i = 1; i <= n; ++i) {
        fa[i] = i;
        int t = read(); pos[t] = i;
        rt[i] = insert(1, n, t);
    }
    for (int i = 1; i <= m; ++i) {
        int x = read(), y = read();
        build(x, y);
    }
    int q = read();
    while (q--) {
        char opt = readc();
        int x = read(), y = read();
        if (opt == 'B') build(x, y);
        else {
            int res = query(rt[getf(x)], 1, n, y);
            if (res == -1) puts("-1");
            else printf("%d\n", pos[res]);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值