LOJ#2018. 「AHOI / HNOI2017」单旋(平衡树模拟+set+线段树)

https://loj.ac/problem/2018

题解:

  • 先考虑只有加点怎么做?设要加的点是第\(x\)大,\(x-1\)的右儿子和\(x+1\)的左儿子一定恰好有一个是空的,加到那里即可。

  • 再模拟一下把最小值\(x\)给splay到根的过程,发现\(x\)到根的链几乎没有变,变得是:把\(x\)的右儿子接到\(x\)的父亲,把原来的根改为\(x\)的右儿子。

  • 再考虑深度的变化,发现除了\(x\)的右儿子那棵子树和\(x\)自己,其它点的深度都+1,我们很容易得到\(x\)的右儿子的值域区间是\((a[x],a[fa[x]])\),线段树区间加即可。

  • 还需要用一个set来加速找一个点的前驱和后继。

  • LL说splay中间的点也是可以做的,但是需要写lct。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 1e5 + 5;

int m, op, n, x;

#define i0 t[i].l
#define i1 t[i].r
struct nod {
	struct tree {
		int l, r;
		ll x, c, lz; 
	} t[N * 100];
	
	int rt, tt;
	
	void jia(int i, ll v) {
		if(i) {
			t[i].x += t[i].c * v;
			t[i].lz += v;
		}
	}
	void down(int i) {
		if(t[i].lz) jia(i0, t[i].lz), jia(i1, t[i].lz), t[i].lz = 0;
	}
	void upd(int i) {
		t[i].c = t[i0].c + t[i1].c;
		t[i].x = t[i0].x + t[i1].x;
	}
	
	int pl, pr; ll px;
	
	void dg(int i, int x, int y) {
		if(y < pl || x > pr) return;
		if(x == y) { px = t[i].x; return;}
		int m = x + y >> 1; down(i);
		dg(i0, x, m); dg(i1, m + 1, y);
	}
	
	int dep(int x) {
		pl = pr = x;
		dg(rt, 1, 1e9);
		return px;
	}
	
	void dd(int &i, int x, int y) {
		if(y < pl || x > pr) return;
		if(!i) i = ++ tt;
		if(x == y) {
			t[i].c = 1; t[i].x = px;
			return;
		}
		int m = x + y >> 1; down(i);
		dd(i0, x, m); dd(i1, m + 1, y);
		upd(i);
	}
	
	void cdep(int x, int y) {
		pl = pr = x, px = y;
		dd(rt, 1, 1e9);
	}
	
	void du(int i, int x, int y) {
		if(y < pl || x > pr || !i) return;
		if(x >= pl && y <= pr) {
			jia(i, px); return;
		}
		int m = x + y >> 1; down(i);
		du(i0, x, m); du(i1, m + 1, y);
		upd(i);
	}
	
	void add(int x, int y, int z) {
		pl = x, pr = y, px = z;
		du(rt, 1, 1e9);
	}
		
	void dt(int &i, int x, int y) {
		if(y < pl || x > pr) return;
		if(x == y) {
			i = 0;
			return;
		}
		int m = x + y >> 1; down(i);
		dt(i0, x, m); dt(i1, m + 1, y);
		upd(i);
	}
	
	void cl(int x) {
		pl = pr = x;
		dt(rt, 1, 1e9);
	}
} tr;

multiset<int> s;
map<int, int> id;
int a[N], t[N][2], fa[N];

int rt;

int add(int x) {
	a[++ n] = x; id[x] = n;
	if(s.empty()) {
		s.insert(x);
		tr.cdep(x, 1);
		rt = n;
		return 1;
	}
	int p = 0, q = 0;
	if((*--s.end()) > x) p = id[*s.upper_bound(x)];
	if((*s.begin()) < x) q = id[*--s.upper_bound(x)];
	int D;
	if(!q || (p && !t[p][0])) {
		fa[n] = p;
		t[p][0] = n;
		D = tr.dep(a[p]);
	} else {
		fa[n] = q;
		t[q][1] = n;
		D = tr.dep(a[q]);
	}
	D ++;
	tr.cdep(x, D);
	s.insert(x);
	return D;
}

int ro(int k) {
	int x = k ? id[(*s.begin())] : id[(*--s.end())];
	if(x == rt) return 1;
	int ans = tr.dep(a[x]);
	tr.add(1, 1e9, 1);
	if(t[x][k]) {
		if(k) {
			tr.add(a[x] + 1, a[fa[x]] - 1, -1);
		} else {
			tr.add(a[fa[x]] + 1, a[x] - 1, -1);
		}
	}
	tr.cdep(a[x], 1);
	fa[t[x][k]] = fa[x]; t[fa[x]][!k] = t[x][k];
	fa[rt] = x; t[x][k] = rt; fa[x] = 0;
	rt = x;
	return ans;
}

void cl(int k) {
	int x = k ? id[(*s.begin())] : id[(*--s.end())];
	s.erase(s.find(a[x]));
	tr.add(1, 1e9, -1);
	tr.cl(a[x]);
	rt = t[x][k]; fa[rt] = 0;
}

int main() {
	scanf("%d", &m);
	fo(ii, 1, m) {
		scanf("%d", &op);
		if(op == 1) {
			scanf("%d", &x);
			pp("%d\n", add(x));
		} else
		if(op == 2) {
			pp("%d\n", ro(1));
		} else
		if(op == 3) {
			pp("%d\n", ro(0));
		} else
		if(op == 4) {
			pp("%d\n", ro(1));
			cl(1);
		} else {
			pp("%d\n", ro(0));
			cl(0);
		}
	}
}
posted @ 2020-06-08 11:50  Cold_Chair  阅读(349)  评论(0编辑  收藏  举报