LOJ#2018. 「AHOI / HNOI2017」单旋(平衡树模拟+set+线段树)
题解:
-
先考虑只有加点怎么做?设要加的点是第\(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);
}
}
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址