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

如果被操作节点还在 一区间节点中,将其在两棵树中都分裂出来。

破坏神$\Longrightarrow$破坏神

先在 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;
}
posted @ 2020-05-23 21:26  Luckyblock  阅读(173)  评论(0编辑  收藏  举报