专栏文章

20251123 Livedream 平衡树

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min29vyj
此快照首次捕获于
2025/12/01 19:23
3 个月前
此快照最后确认于
2025/12/01 19:23
3 个月前
查看原文

总结

期望: 100 + 100 + 0 = 200
实际: 100 + 100 + 0 = 200 rk2

T1 P1503 鬼子进村

考虑到对于一个兵能到哪些位置就是考虑前面一个被摧毁和后面一个被摧毁的位置,即前驱后继。即用平衡树维护,fhq 非常的好写。
codeCPP
#include <bits/stdc++.h>
#define i64 long long

using namespace std;

const i64 kMaxN = 1e5 + 5;

struct E {
	struct Node {
	    int v, ls, rs, sz, rk;
	} t[kMaxN];
	int tot, rt;
	
	void update(int x) {
	    t[x].sz = t[t[x].ls].sz + t[t[x].rs].sz + 1;
	}
	int add(int val) {
	    t[++tot] = Node{val, 0, 0, 1, rand()};
	    return tot;
	}
	void split(int x, int& a, int& b, int val) {
	    if (x == 0) {
	        return a = b = 0, void();
	    }
	    if (t[x].v <= val) {
	        a = x, split(t[x].rs, t[x].rs, b, val);
	    } else {
	        b = x, split(t[x].ls, a, t[x].ls, val);
	    }
	    update(x);
	}
	void merge(int &x, int a, int b) {
	    if (a == 0 || b == 0) {
	        return x = a + b, void();
	    }
	    if (t[a].rk > t[b].rk) {
	        x = a, merge(t[a].rs, t[a].rs, b);
	    } else {
	        x = b, merge(t[b].ls, a, t[b].ls);
	    }
	    update(x);
	}
	void insert(int &x, int val) {
	    int v = add(val), a = 0, b = 0;
	    split(x, a, b, val);
	    merge(a, a, v);
	    merge(x, a, b);
	}
	void del(int &x, int val) {
	    int a = 0, b = 0, c = 0;
	    split(x, a, b, val);
	    split(a, a, c, val - 1);
	    merge(c, t[c].ls, t[c].rs);
	    merge(a, a, c);
	    merge(x, a, b);
	}
	int find(int x, int val) {
	    for (; t[t[x].ls].sz + 1 != val; ) {
	        if (t[t[x].ls].sz >= val) x = t[x].ls;
	        else val -= t[t[x].ls].sz + 1, x = t[x].rs; 
	    }
	    return t[x].v;
	}
	int rnk(int &x, int val) {
	    int a = 0, b = 0;
	    split(x, a, b, val - 1);
	    int v = t[a].sz + 1;
	    merge(x, a, b);
	    return v;
	}
	int prev(int &x, int val) {
	    int a = 0, b = 0;
	    split(x, a, b, val);
	    int v = find(a, t[a].sz);
	    merge(x, a, b);
	    return v;
	}
	int suc(int &x, int val) {
	    int a = 0, b = 0;
	    split(x, a, b, val - 1);
	    int v = find(b, 1);
	    merge(x, a, b);
	    return v;
	}
} t;

int st[kMaxN], tot, n, m;

signed main() {
    ios ::sync_with_stdio(0), cin.tie(0);
    srand(time(0));
    cin >> n >> m, t.rt = 0;
    t.insert(t.rt, 0), t.insert(t.rt, n + 1);
	for (int i = 1; i <= m; i++) {
		char c; cin >> c;
		if (c == 'D') {
			int x; cin >> x, st[++tot] = x;
			t.insert(t.rt, x);
		} else if (c == 'R') {
			int x = st[tot]; tot--;
			t.del(t.rt, x);
		} else {
			int x; cin >> x;
			int l = t.prev(t.rt, x), r = t.suc(t.rt, x);
			cout << (l == r ? 0 : r - l - 1) << '\n';
		}
	}
    return 0;
}

T2 P2572 [SCOI2010] 序列操作

赛时想法:线段树 平衡树方法:
  1. 维护按 size 劈的 fhq;
  2. 维护赋值标记 tag, 取反标记 rev;
  3. 维护 pushdown函数,先下传赋值标记,再下穿取反标记,清空;
  4. 在 split 与 merge 诋毁前要将标记下传;
  5. 维护前缀0/1,后缀0/1,最长0/1 与线段树同理;
赛时codeCPP
#include <bits/stdc++.h>
#define ls(x) x << 1
#define rs(x) x << 1 | 1

using namespace std;

const int kMaxN = 1e5 + 5;

struct N {
	int cnt[2], maxl[2], maxr[2], mx[2], tg[2], len;
	N() {
		cnt[0] = cnt[1] = maxl[0] = maxr[0] = mx[0] = mx[1] = tg[1] = len = 0; 
		tg[0] = -1;
	}
} t[kMaxN << 2];

int a[kMaxN], n, m;

void add1(int x) { //反转 
	t[x].tg[1] ^= 1;
	swap(t[x].cnt[0], t[x].cnt[1]);
	swap(t[x].maxl[0], t[x].maxl[1]);
	swap(t[x].maxr[0], t[x].maxr[1]);
	swap(t[x].mx[0], t[x].mx[1]);
}

void add2(int x, int op) { //赋值 0 1 
	t[x].tg[0] = op, t[x].tg[1] = 0;
	t[x].cnt[op] = t[x].maxl[op] = t[x].maxr[op] = t[x].mx[op] = t[x].len;
	t[x].cnt[!op] = t[x].maxl[!op] = t[x].maxr[!op] = t[x].mx[!op] = 0;
}

N Mix(N A, N B) {
	N C;
	C.len = A.len + B.len;
	C.tg[0] = -1, C.tg[1] = 0;
	for (int i = 0; i <= 1; i++) {	
		C.cnt[i] = A.cnt[i] + B.cnt[i];
		C.mx[i] = max({A.mx[i], B.mx[i], A.maxr[i] + B.maxl[i]});
		C.maxl[i] = A.maxl[i];
		if (A.cnt[i] == A.len) C.maxl[i] = A.len + B.maxl[i];
		C.maxr[i] = B.maxr[i];
		if (B.cnt[i] == B.len) C.maxr[i] = B.len + A.maxr[i]; 
	}
	return C;
}

void pushup(int x) {
	t[x] = Mix(t[ls(x)], t[rs(x)]); 
}

void pushdown(int x) {
	if (t[x].tg[0] != -1) {
		add2(ls(x), t[x].tg[0]);
		add2(rs(x), t[x].tg[0]);
		t[x].tg[0] = -1;
	}
	if (t[x].tg[1]) {
		add1(ls(x));
		add1(rs(x));
		t[x].tg[1] = 0;
	}
}

void build(int x, int l, int r) {
	if (l == r) {
		t[x].cnt[a[l]] = t[x].maxl[a[l]] = t[x].maxr[a[l]] = t[x].mx[a[l]] = t[x].len = 1;
		t[x].tg[0] = -1, t[x].tg[1] = 0;
		return;
	}
	int mid = l + r >> 1;
	build(ls(x), l, mid);
	build(rs(x), mid + 1, r);
	pushup(x);
}

void update(int x, int l, int r, int L, int R, int op) {
	if (L <= l && r <= R) {
		if (op == 0 || op == 1) add2(x, op);
		else add1(x);
		return;
	}
	int mid = l + r >> 1;
	pushdown(x);
	if (L <= mid) update(ls(x), l, mid, L, R, op);
	if (mid < R) update(rs(x), mid + 1, r, L, R, op); 
	pushup(x);
} 

N query(int x, int l, int r, int L, int R) {
	if (L <= l && r <= R) {
		return t[x];
	}
	pushdown(x);
	int mid = l + r >> 1, f1 = (L <= mid), f2 = (mid < R);
	if (f1 && !f2) return query(ls(x), l, mid, L, R);
	if (!f1 && f2) return query(rs(x), mid + 1, r, L, R);
	if (f1 && f2) return Mix(query(ls(x), l, mid, L, R), query(rs(x), mid + 1, r, L, R));
	return N();
}

signed main() {
	ios:: sync_with_stdio(0), cin.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	build(1, 1, n);
	for (int i = 1; i <= m; i++) {
		int op, l, r; cin >> op >> l >> r, l++, r++;
		if (op <= 2) update(1, 1, n, l, r, op);
		else if (op == 3) cout << query(1, 1, n, l, r).cnt[1] << '\n';
		else cout << query(1, 1, n, l, r).mx[1] << '\n';
	}
	return 0;
}

T3 [APIO2015] 巴邻旁之桥

  1. 若一个人的两点在同一边,则不需要决策,可以直接算答案
  • 情况1:k=1k = 1
  • 设位置为 pp, 则第 ii 个人的通勤距离为 sip+1+Tip\left | s_i - p \right | + 1 + \left | T_i - p \right | ,其中 cntcnt 表示再河的两边的人数;
  • 问题转换为 2×cnt2 \times cnt,寻找一个位置 pp,是的所有点到 p 的距离最小。即中位数;
  • 情况2:k=2k = 2
  • 设桥的位置为 pp, qq,当 sis_i, tit_i 中间有桥时,贡献为 siti+1\left | s_i - t_i \right | + 1
  • psitiqp \le s_i \le t_i \le q,则 sipqTis_i - p \le q - Ti 时,会去 p 桥,否则去 q 桥。移项 si+tip+qs_i + t_i \le p + q 时,选 p 桥;
  • 当 p q 确定是,按 si+tis_i + t_i 排序,选 p 的一定时排序的一段前缀;
  • 对于 cnt 个人,枚举分解先,左侧的人去 p,右侧的人去 q。即转化为情况1;
  • 维护两个平衡树,分别存前后点的位置,动态删除与插入,找中位数即可;
codeCPP
#include <bits/stdc++.h>
#define i64 long long
#define int long long

using namespace std;

const i64 kMaxN = 5e5 + 5, INF = 1e18;

struct E {
	struct Node {
	    int v, ls, rs, sz, rk, sum;
	} t[kMaxN];
	int tot, rt;
	
	void update(int x) {
	    t[x].sz = t[t[x].ls].sz + t[t[x].rs].sz + 1;
	    t[x].sum = t[t[x].ls].sum + t[t[x].rs].sum + t[x].v;
	}
	int add(int val) {
	    t[++tot] = Node{val, 0, 0, 1, rand(), val};
	    return tot;
	}
	void split(int x, int& a, int& b, int val) {
	    if (x == 0) {
	        return a = b = 0, void();
	    }
	    if (t[x].v <= val) {
	        a = x, split(t[x].rs, t[x].rs, b, val);
	    } else {
	        b = x, split(t[x].ls, a, t[x].ls, val);
	    }
	    update(x);
	}
	void merge(int &x, int a, int b) {
	    if (a == 0 || b == 0) {
	        return x = a + b, void();
	    }
	    if (t[a].rk > t[b].rk) {
	        x = a, merge(t[a].rs, t[a].rs, b);
	    } else {
	        x = b, merge(t[b].ls, a, t[b].ls);
	    }
	    update(x);
	}
	void insert(int &x, int val) {
	    int v = add(val), a = 0, b = 0;
	    split(x, a, b, val);
	    merge(a, a, v);
	    merge(x, a, b);
	}
	void del(int &x, int val) {
	    int a = 0, b = 0, c = 0;
	    split(x, a, b, val);
	    split(a, a, c, val - 1);
	    merge(c, t[c].ls, t[c].rs);
	    merge(a, a, c);
	    merge(x, a, b);
	}
	int find(int x, int val) {
//		assert(val != 0);
		if (val == 0) return 0;
	    for (; t[t[x].ls].sz + 1 != val; ) {
	        if (t[t[x].ls].sz >= val) x = t[x].ls;
	        else val -= t[t[x].ls].sz + 1, x = t[x].rs; 
	    }
	    return t[x].v;
	}
	int sum(int &x, int val, int ret = 0) {
		int a = 0, b = 0;
		split(x, a, b, val);
		ret -= t[a].sum - t[a].sz * val;
		ret -= t[b].sz * val - t[b].sum;
		merge(x, a, b);
		return ret;
	}
} t1, t2;

int s[kMaxN], t[kMaxN], a[kMaxN], tot, n, k, ans;

bool cmp(int x, int y) {
	return s[x] + t[x] < s[y] + t[y];
}

signed main() {
    ios ::sync_with_stdio(0), cin.tie(0);
    srand(time(0));
	cin >> k >> n;
	for (int i = 1; i <= n; i++) {
		char op1, op2; int x, y;
		cin >> op1 >> x >> op2 >> y;
		if (op1 == op2) ans += abs(y - x);
		else {
			++tot, ans++;
			s[tot] = x, t[tot] = y;
			a[tot] = tot;
		}
	}
	sort(a + 1, a + tot + 1, cmp);
	t1.rt = t2.rt = 0;
	for (int i = 1; i <= tot; i++) {
		t2.insert(t2.rt, s[a[i]]), t2.insert(t2.rt, t[a[i]]);
	}
	int r = INF;
	if (k == 1) {
		int p = t2.find(t2.rt, tot);
		r = t2.sum(t2.rt, p);
	} else {
		int p = t2.find(t2.rt, tot);
		r = t2.sum(t2.rt, p);
		for (int i = 1; i < tot; i++) {
			t2.del(t2.rt, s[a[i]]), t2.del(t2.rt, t[a[i]]);
			t1.insert(t1.rt, s[a[i]]), t1.insert(t1.rt, t[a[i]]);
			int p = (t1.find(t1.rt, i) + t1.find(t1.rt, i + 1)) / 2;
			int q = (t2.find(t2.rt, tot - i) + t2.find(t2.rt, tot - i + 1)) / 2;
			r = min(r, t1.sum(t1.rt, p) + t2.sum(t2.rt, q));
			
		}
	}
	cout << ans + r << '\n';
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...