Splay + 将一个区间合并为一个点,类似P3960NOIP07列队
用STL的map存被合并区间的右端点对应的节点下标,查询节点下标时lower_bound即可
点击查看代码
#include <map>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
constexpr int N = 5e6 + 5;
int n, q;
std::map<int, int> map;
struct Node {
int son[2], fa;
int l, r; // 这段区间存的是编号[l,r]的节点
int size;
} tr[N];
int root, idx;
inline void push_up(int u) {
tr[u].size = tr[tr[u].son[0]].size + tr[tr[u].son[1]].size + (tr[u].r - tr[u].l + 1);
}
void rotate(int x) {
int y = tr[x].fa, z = tr[y].fa, k = tr[y].son[1] == x;
tr[tr[z].son[tr[z].son[1] == y] = x].fa = z;
tr[tr[y].son[k] = tr[x].son[!k]].fa = y;
tr[tr[x].son[!k] = y].fa = x;
push_up(y), push_up(x);
}
void splay(int x, int rt) {
while(tr[x].fa != rt) {
int y = tr[x].fa, z = tr[y].fa;
if(z != rt) rotate((tr[y].son[1] == x) ^ (tr[z].son[1] == y) ? x : y);
rotate(x);
}
if(!rt) root = x;
}
// 将节点u分裂:u保留<=k的,返回一个新的节点(后若干)
int split(int u, int k) {
int v = ++ idx;
tr[v].l = k + 1, tr[v].r = tr[u].r;
tr[u].r = k;
if(tr[u].son[1]) {
int t = tr[u].son[1];
while(tr[t].son[0]) t = tr[t].son[0];
tr[tr[t].son[0] = v].fa = t;
} else tr[tr[u].son[1] = v].fa = u;
splay(v, 0);
return v;
}
// 将编号为id的节点拉出来,返回其编号,更新其余块(不更新自己)
int split3(int id) {
std::map<int, int>::iterator it = map.lower_bound(id);
if(it == map.end()) exit(0x7fffffff);
int u = it->second; // 得到id所在块
if(id < tr[u].r) it->second = split(u, id); // 右边分裂
else map.erase(it); // 没有右边了:暂时删除
if(id > tr[u].l) map[id - 1] = u, u = split(u, id - 1); // 左边分裂,左边单独为一块
return u;
}
// 得到排名
int getrnk(int u) {
splay(u, 0); // 旋转到根,左子树大小
return tr[tr[u].son[0]].size + 1;
}
// 重命名id为newid
int rename(int id, int newid) {
int u = split3(id); // 找到这个节点
map[tr[u].l = tr[u].r = newid] = u; // 更新id
return getrnk(u);
}
// “暂时”删除
void remove_temp(int u) {
splay(u, 0);
tr[tr[u].son[0]].fa = tr[tr[u].son[1]].fa = 0;
if(tr[u].son[0]) {
int t = tr[u].son[0];
while(tr[t].son[1]) t = tr[t].son[1];
splay(t, 0);
tr[tr[t].son[1] = tr[u].son[1]].fa = t;
push_up(t);
} else root = tr[u].son[1];
}
// 移到最前面
int move_to_front(int id) {
int u = split3(id); // 找到这个节点
map[tr[u].l] = u; // 此时必有tr[u].l=tr[u].r;更新id
int ret = getrnk(u);
remove_temp(u);
tr[u].son[0] = tr[u].son[1] = tr[u].fa = 0;
int v = root;
while(tr[v].son[0]) v = tr[v].son[0];
tr[tr[v].son[0] = u].fa = v;
splay(u, 0);
return ret;
}
// 移到最后面
int move_to_back(int id) {
int u = split3(id);
map[tr[u].l] = u;
int ret = getrnk(u);
remove_temp(u);
tr[u].son[0] = tr[u].son[1] = tr[u].fa = 0;
int v = root;
while(tr[v].son[1]) v = tr[v].son[1];
tr[tr[v].son[1] = u].fa = v;
splay(u, 0);
return ret;
}
// 根据排名得到编号
int getid(int k) {
int u = root, lssz;
while(u) {
lssz = tr[tr[u].son[0]].size;
if(lssz >= k) u = tr[u].son[0];
else if(lssz + (tr[u].r - tr[u].l + 1) >= k) return tr[u].l + (k - lssz) - 1;
else k -= lssz + (tr[u].r - tr[u].l + 1), u = tr[u].son[1];
}
return 0; // 不存在
}
// 初始化
void init() {
root = ++ idx;
tr[idx].l = 1, tr[idx].r = n, tr[idx].size = n;
map[n] = idx;
}
int main() {
scanf("%d%d", &n, &q), init();
for(int i = 1, op, x, y, a = 0; i <= q; i ++) {
scanf("%d%d", &op, &x);
if(op == 1) scanf("%d", &y), a = rename(x - a, y - a);
else if(op == 2) a = move_to_front(x - a);
else if(op == 3) a = move_to_back(x - a);
else a = getid(x - a);
printf("%d\n", a);
}
return 0;
}