P3285 [SCOI2014]方伯伯的OJ
知识点:平衡树,动态开点
原题面
题目要求
给定一初始长度为n的序列,其中元素从 \(1\sim n\) 依次编号。
给定下列五种操作 :
\(\text{1}\ x\ y\) :将编号为x的元素 编号改为 y,输出该元素在序列中的排名。
\(\text{2}\ x\) :将编号x的元素 置顶,输出执行该操作前编号为x元素的排名。
\(\text{3}\ x\) :将编号x的元素 置底,,输出执行该操作前编号为x元素的排名。
\(\text{4}\ x\):询问排名为x的元素的编号。
强制在线
\(1\le n\le 10^8, 1\le m \le 10^5, 1 \le y \le 2 \times 10^8\)
分析题意
?这和P2596 [ZJOI2006]书架不是一个题
?怎么就变成黑题了
!看我秒切黑题
\(\text{Memory Limit Exceeded}\)
请勿混淆 排名,编号,节点编号 三者的概念。
排名 为 某序列元素 在 序列中的次序。
编号/元素编号 意义与题面中"编号"意义相同,为 某序列元素被标记的号码。
节点编号 为 某平衡树节点 的 node_num,在插入新节点时被赋予,恒定不变。
基本思想
与 [ZJOI2006]书架 一致,均是建立映射关系,维护元素编号 对应的 节点编号。
但数据范围巨大,不可直接套用做法。
发现有许多元素根本访问不到,考虑动态开点,
将一个连续的区间 压缩成一个节点。
struct SplayNode {
int son[2], fa, size, cnt; //cnt区间长度
int l, r; //l,r 为区间左右端点。
形态如下,中序遍历仍然是原序列。
开两棵动态开点平衡树。
- 一棵平衡树 t 用于查询排名,回答询问。
压缩 元素编号。
其结构 维护在序列中的排名。
其中序遍历 即为给定序列。 - 一棵平衡树 t1 用于建立 元素编号与 t中节点编号 的映射关系。
其结构 维护元素编号, 其中序遍历 必为 1~ n。
节点储存 其压缩的元素编号 在 t 中的节点编号。
操作1
如果被操作节点还在 一区间节点中,将其在两棵树中都分裂出来。
先在 t1 中找到 x 在 t 中对应的节点编号。
回到 t 中,将对应节点的 l,r值替换为 y。
将 编号x在 t1中对应节点删除,插入编号y的新节点。 使新节点与原节点 t1中节点编号一致。
注意维护结构,保证中序遍历不变。
查询排名时,将t中对应节点旋转至根,输出其左子树大小 + 1。
操作 2,3
先分裂出对应节点,在t中将其旋转至根。
需要输出的查询排名 即为 左子树大小 + 1。
操作方式与 P2596 [ZJOI2006]书架 - Luckyblock相同。
操作2将左子树挂到右子树最左侧。 操作3将右子树挂到左子树最右侧。
注意特判 根有无左右子树。
操作 4
经典查询 第k大值操作,直接在 t 中按照排名查询即可。
注意
具体分裂方式 详见代码。
t1 功能单一可使用map替代,详见锣鼓题解。太懒了就没写。
代码实现
//
/*
By:Luckyblock
Splay 区间压缩成点。
*/
#include <cstdio>
#include <ctype.h>
#define ll long long
#define fat (t[now].fa)
#define ls (t[now].son[0])
#define rs (t[now].son[1])
const int kMaxn = 1e6 + 10;
//=============================================================
struct SplayNode {
int son[2], fa, size, cnt;
int l, r;
} t[kMaxn << 1], t1[kMaxn << 1];
int n, m, node_num, node_num1, root, root1, ans;
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if(ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Pushup1(int now) {
if (! now) return ;
t1[now].size = t1[now].cnt + t1[t1[now].son[0]].size + t1[t1[now].son[1]].size;
}
bool GetSonNum1(int now) {
return now == t1[t1[now].fa].son[1];
}
void Rotate1(int now)
{
int fa = t1[now].fa, gfa = t1[fa].fa, son_num = GetSonNum1(now);
t1[fa].son[son_num] = t1[now].son[son_num ^ 1];
t1[t1[now].son[son_num ^ 1]].fa = fa;
t1[now].son[son_num ^ 1] = fa, t1[fa].fa = now;
t1[now].fa = gfa;
if (gfa) t1[gfa].son[fa == t1[gfa].son[1]] = now;
Pushup1(fa), Pushup1(now);
}
void Splay1(int now) {
for (int fa = t1[now].fa; (fa = t1[now].fa) != 0; Rotate1(now))
if (t1[fa].fa) Rotate1(GetSonNum1(now) == GetSonNum1(fa) ? fa : now);
root1 = now;
}
void Insert1(int id, int value)
{
int now = root1, fa = 0;
while (true) {
fa = now, now = t1[now].son[t1[now].r < value];
if (! now) {
t1[id].son[0] = t1[id].son[1] = 0;
t1[id].fa = fa;
t1[id].l = t1[id].r = value;
t1[id].cnt = t1[id].size = 1;
t1[fa].son[t1[fa].r < value] = id;
Pushup1(fa), Pushup1(id); Splay1(id);
return ;
}
}
}
int QueryPrecursor1() {
int now = t1[root1].son[0];
while(t1[now].son[1]) now = t1[now].son[1];
return now;
}
void Delete1(int pos) {
Splay1(pos);
if (! t1[root1].son[0]) {
root1 = t1[root1].son[1], t1[root1].fa = 0;
return ;
}
if (! t1[root1].son[1]) {
root1 = t1[root1].son[0], t1[root1].fa = 0;
return ;
}
int left_max = QueryPrecursor1(), old_root1 = root1;
Splay1(left_max);
t1[root1].son[1] = t1[old_root1].son[1], t1[t1[old_root1].son[1]].fa = root1;
Pushup1(root1);
}
int Split1(int now, int x) {
Splay1(now);
SplayNode ori = t1[now];
int left = now, middle = ++ node_num1, right = ++ node_num1;
t1[left].l = ori.l, t1[left].r = x - 1;
t1[left].cnt = t1[left].r - t1[left].l + 1;
t1[left].son[0] = ori.son[0], t1[t1[left].son[0]].fa = left;
t1[left].fa = middle; t1[left].son[1] = 0;
t1[right].l = x + 1, t1[right].r = ori.r;
t1[right].cnt = t1[right].r - t1[right].l + 1;
t1[right].son[1] = ori.son[1], t1[t1[right].son[1]].fa = right;
t1[right].fa = middle; t1[right].son[0] = 0;
t1[middle].l = t1[middle].r = x;
t1[middle].cnt = 1;
t1[middle].son[0] = left, t1[middle].son[1] = right;
t1[middle].fa = ori.fa;
Pushup1(left), Pushup1(right), Pushup1(middle);
root1 = middle;
return middle;
}
int QueryPos(int x) {
int now = root1;
while (true) {
if (t1[now].l <= x && x <= t1[now].r) return now;
now = t1[now].son[x > t1[now].r];
}
}
void Modify1(int posx, int y) {
Delete1(posx); Insert1(posx, y);
}
void Pushup(int now) {
if (! now) return ;
t[now].size = t[now].cnt + t[ls].size + t[rs].size;
}
bool GetSonNum(int now) {
return now == t[fat].son[1];
}
void Rotate(int now)
{
int fa = fat, gfa = t[fa].fa, son_num = GetSonNum(now);
t[fa].son[son_num] = t[now].son[son_num ^ 1];
t[t[now].son[son_num ^ 1]].fa = fa;
t[now].son[son_num ^ 1] = fa, t[fa].fa = now;
t[now].fa = gfa;
if (gfa) t[gfa].son[fa == t[gfa].son[1]] = now;
Pushup(fa), Pushup(now);
}
void Splay(int now) {
for (int fa = fat; (fa = fat) != 0; Rotate(now))
if (t[fa].fa) Rotate(GetSonNum(now) == GetSonNum(fa) ? fa : now);
root = now;
}
int Split(int now, int x) {
Splay(now);
SplayNode ori = t[now];
int left = now, middle = ++ node_num, right = ++ node_num;
t[left].l = ori.l, t[left].r = x - 1;
t[left].cnt = t[left].r - t[left].l + 1;
t[left].son[0] = ori.son[0], t[t[left].son[0]].fa = left;
t[left].fa = middle; t[left].son[1] = 0;
t[right].l = x + 1, t[right].r = ori.r;
t[right].cnt = t[right].r - t[right].l + 1;
t[right].son[1] = ori.son[1], t[t[right].son[1]].fa = right;
t[right].fa = middle; t[right].son[0] = 0;
t[middle].l = t[middle].r = x;
t[middle].cnt = 1;
t[middle].son[0] = left, t[middle].son[1] = right;
t[middle].fa = ori.fa;
Pushup(left), Pushup(right), Pushup(middle);
root = middle;
return middle;
}
bool block(int x) {
int posx = QueryPos(x);
return t[posx].l == t[posx].r;
}
void Modify(int x, int y) {
int posx = QueryPos(x);
if (block(x)) {
t[posx].l = t[posx].r = y; Modify1(posx, y);
return ;
}
int mid = Split(posx, x), mid1 = Split1(posx, x);
t[mid].l = t[mid].r = y; Modify1(mid1, y);
}
void PutTop(int now) {
Splay(now);
if(! t[now].son[0]) return ; //特判
if(! t[now].son[1]) {
t[now].son[1] = t[now].son[0], t[now].son[0] = 0;
return ;
}
int suc = t[root].son[1];
while(t[suc].son[0]) suc = t[suc].son[0];
t[suc].son[0] = t[root].son[0];
t[t[root].son[0]].fa = suc;
t[root].son[0] = 0;
Splay(suc);
}
void PutBottom(int now) {
Splay(now);
if(! t[now].son[1]) return ; //特判
if(! t[now].son[0]) {
t[now].son[0] = t[now].son[1], t[now].son[1] = 0;
return ;
}
int pre = t[root].son[0];
while(t[pre].son[1]) pre = t[pre].son[1];
t[pre].son[1] = t[root].son[1];
t[t[root].son[1]].fa = pre;
t[root].son[1] = 0;
Splay(pre);
}
int Kth(int rank) {
int ret, now = root;
while (true) {
if(rank <= t[t[now].son[0]].size) {
now = t[now].son[0];
continue;
}
if(rank <= t[t[now].son[0]].size + t[now].cnt) {
rank -= t[t[now].son[0]].size;
ret = now;
break;
}
rank -= (t[t[now].son[0]].size + t[now].cnt);
now = t[now].son[1];
}
if(t[ret].cnt == 1) return t[ret].l;
return t[ret].l + rank - 1;
}
int QueryRank(int x) {
int posx = QueryPos(x);
Splay(posx);
return t[t[posx].son[0]].size + 1;
}
//=============================================================
int main() {
n = read(), m = read();
root = root1 = 1; //初始值
t[++ node_num] = t1[++ node_num1] = (SplayNode) {0, 0, 0, n, n, 1, n};
for (int i = 1; i <= m; ++ i) {
int opt = read(), x = read() - ans, y;
if (opt == 1) {
y = read() - ans, Modify(x, y);
printf("%d\n", (ans = QueryRank(y)));
}
if (opt == 2) {
if(! block(x)) Modify(x, x);
printf("%d\n", (ans = QueryRank(x)));
PutTop(QueryPos(x));
}
if (opt == 3) {
if(! block(x)) Modify(x, x);
printf("%d\n", (ans = QueryRank(x)));
PutBottom(QueryPos(x));
}
if (opt == 4) {
printf("%d\n", (ans = Kth(x)));
}
}
return 0;
}