P3850 [TJOI2007]书架

知识点: 平衡树

原题面

题目要求:

给定一长度为 \(n\) 的序列。
支持 \(m\) 次单点插入,\(q\)次 单点查询。
\(1\le N\le 200, 1 \le M \le 10^5,1\le M\le 10^5\)


分析题意

是道水题。

平衡树结构 维护权值在序列中的排名。

现有一数列 1 9 2 6 0 8 1' 7

破坏神

如图,图中节点上的数 为权值。
平衡树结构 维护权值在序列中的排名。
将平衡树中序遍历后 即可得原序列。

将某节点旋转至根节点后,
由中序遍历的性质, 某节点权值在原序列中的位置,
为总节点数 \(-\) 其右子树大小。
如 8 在原序列中的位置 \(6 = 8 - 2\)

由性质,则可在平衡树上 dfs。
以k为初始值,不断减去左子树大小,即可求得 原排名为k的权值。

将w插入至k位置,即将w置于 排名为k-1的权值之后,成为它的后继。
在平衡树中,即令w成为 排名为k-1的权值为根的子树 右子树中最左侧的结点。
代码实现时插入了空节点"Marisa" 代表排名为0的节点 (无歧义)


代码实现

Splay

//Splay
/*
By:Luckyblock
Typical Splay
*/
#include <cstdio> 
#include <string> 
#include <ctype.h>
#include <iostream>
#include <algorithm>
#define Fat (t[now].Fa)
#define ls (t[now].Son[0])
#define rs (t[now].Son[1])
#define ll long long
const int MARX = 1e6 + 10;
//=============================================================
struct SplayNode {
	int Fa, Son[2], Size;
	std :: string Val;
} t[MARX];
int N, M, Q, NodeNum, Root;
//=============================================================
inline int read() {
  int s = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') s = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
  return s * w; 
}
bool GetSonNum(int now) {
  return t[Fat].Son[1] == now;
}
void Pushup(int now)  {
	if (now) {
	  t[now].Size = 1;
	  if (ls) t[now].Size += t[ls].Size;
	  if (rs) t[now].Size += t[rs].Size;
	}
}
void Rotate(int now) {
	int fa = Fat, gfa = t[fa].Fa, WhichSon = GetSonNum(now);
	t[fa].Son[WhichSon] = t[now].Son[WhichSon ^ 1];
  t[t[fa].Son[WhichSon]].Fa = fa;
	t[now].Son[WhichSon ^ 1] = fa;
	t[fa].Fa = now, t[now].Fa = gfa;
	if (gfa) t[gfa].Son[t[gfa].Son[1] == fa] = 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 Kth(int rank) {
	int now = Root;
	while(1) {
	  if (ls && rank <= t[ls].Size) {
	   now = ls; 
    } else {
	  	int tmp = (ls ? t[ls].Size : 0) + 1;
	  	if (rank <= tmp) return now;
	  	rank -= tmp; now = rs;
	  }
	}
}
void Insert(std :: string val, int rank) {
  Splay(Kth(rank - 1));
  int now = t[Root].Son[1];
  if (! now) {
    t[++ NodeNum] = (SplayNode) {Root, 0, 0, 1, val};
    t[Root].Son[1] = NodeNum;
    Splay(NodeNum);
    return ;
  }
  
  while (t[now].Son[0]) {
    now = t[now].Son[0];
  }
  
	t[++ NodeNum] = (SplayNode) {now, 0, 0, 1, val};
  t[now].Son[0] = NodeNum;
	Splay(NodeNum);
}
//=============================================================
int main() {
        t[++ NodeNum] = (SplayNode) {0, 0, 0, 1, "Marisa"};
	Root = NodeNum;
        N = read();
	for(int i = 1; i <= N; ++ i) {
	  std :: string val; std :: cin >> val;
	  Insert(val, i + 1);
	}
	M = read();
	for (int i = 1; i <= M; ++ i) {
	  std :: string val; std :: cin >> val;
	  int rank = read() + 2;
	  Insert(val, rank);
  }
  Q = read();
  for (int i = 1; i <= Q; ++ i) {
    int rank = read();
    std :: cout << t[Kth(rank + 2)].Val << "\n";
  }
	return 0;
}

Fhq-Treap

直接按子树大小分裂即可。

//fhq-treap
/*
By:Luckyblock
*/
#include <cstdio>
#include <string>
#include <cstdlib>
#include <ctype.h>
#include <iostream>
#define ll long long
const int MARX = 2e5 + 10;
//=============================================================
struct FhqTreapNode
{
	int son[2], size, Rand;
	std :: string val;
} t[MARX];
int NodeNum, Root;
int tmp1, tmp2, tmp3;
//=============================================================
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 Update(int x) {t[x].size = t[t[x].son[0]].size + t[t[x].son[1]].size + 1;}
int NewNode(std :: string Val)
{
	NodeNum ++;
	t[NodeNum].size = 1; t[NodeNum].val = Val; t[NodeNum].Rand = rand();
	return NodeNum;
}
int Merge(int x, int y)
{
	if(! x || ! y) return x + y;
	if(t[x].Rand < t[y].Rand)
	{
	  t[x].son[1] = Merge(t[x].son[1], y); Update(x);
	  return x;
	}
	t[y].son[0] = Merge(x, t[y].son[0]); Update(y);
	return y;
}
void Split(int now, int Size, int &x, int &y)
{
	if(! now) {x = y = 0; return ;}
	if(Size <= t[t[now].son[0]].size) y = now, Split(t[now].son[0], Size, x, t[now].son[0]);
	else x = now, Split(t[now].son[1], Size - t[t[now].son[0]].size - 1, t[now].son[1], y);
	Update(now);
}
int Kth(int now, int Rank)
{
	while(1)
	{
	  if(Rank <= t[t[now].son[0]].size) {now = t[now].son[0]; continue;}
	  if(Rank == t[t[now].son[0]].size + 1) return now;
	  Rank -= t[t[now].son[0]].size + 1, now = t[now].son[1];
	}
}
void Insert(int Size, std :: string Val)
{
	Split(Root, Size, tmp1, tmp2);
	Root = Merge(Merge(tmp1, NewNode(Val)), tmp2);
}
//=============================================================
int main()
{
	srand(114514);
	int N = read();
	for(int i = 1; i <= N; ++ i)
	{
	  std :: string Val; std :: cin >> Val;
	  Insert(i, Val);
	}
	int M = read();
	for(int i = 1; i <= M; ++ i)
	{
	  std :: string Val; int Pos;
	  std :: cin >> Val; std :: cin >> Pos;
	  Insert(Pos, Val);
	}
	int Q = read();
	for(int i = 1; i <= Q; ++ i)
	{
	  int Pos = read() + 1;
	  std :: cout << t[Kth(Root, Pos)].val << '\n';
	}
	return 0;
}
posted @ 2020-05-23 10:38  Luckyblock  阅读(172)  评论(6编辑  收藏  举报