线段树合并和主席树不同的是。主席树的节点还要依赖之前的树结构。而线段树合并是若干个分离的线段树合并起来,在创建节点的时候有些不同。
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;
}