[TJOI2007]书架

题目

网上搜

分析

我们可以认为插入一本书是在树中第 \(k\) 的位置进行插入操作
其中 \(k\) 为这本放入书架后的位置
考虑 \(fhq-treap\) 实现
我们将书编号为 \([0,n-1]\)
那么如果插入的书的位置为 \(k\),实际上是在树中位置为 \(k+1\)
将前 \(k\) 个分裂出来就可以插入,再合并还原即可
询问操作就是查第 \(k\)

\(Code\)

#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;

const int N = 1e5 + 500;
int n, m, q, rt, tot;
char s[N][20];
struct node{
	int ls, rs, siz, val, rnd;
}tr[N];

int read()
{
	int res = 0; char ch = getchar();
	while (ch < '0' || ch > '9') ch = getchar();
	while (ch >= '0' && ch <= '9') 
		res=(res<<3)+(res<<1)+ch-'0', ch = getchar();
	return res; 
} 

void pushup(int p){tr[p].siz = tr[tr[p].ls].siz + tr[tr[p].rs].siz + 1;}

int new_node(int v)
{
	static int size = 0;
	tr[++size] = node{0, 0, 1, v, rand()};
	return size;
}

void split(int p, int k, int &x, int &y)
{
	if (!p) x = y = 0;
	else{
		if (k <= tr[tr[p].ls].siz)
			y = p, split(tr[p].ls, k, x, tr[p].ls);
		else x = p, split(tr[p].rs, k - tr[tr[p].ls].siz - 1, tr[p].rs, y);
		pushup(p);
	}
}

int merge(int x, int y)
{
	if (!x || !y) return x + y;
	if (tr[x].rnd < tr[y].rnd)
	{
		tr[x].rs = merge(tr[x].rs, y);
		pushup(x); return x;
	}
	else{
		tr[y].ls = merge(x, tr[y].ls);
		pushup(y); return y;
	}
}

void insert(int k, int v)
{
	int a, b;
	split(rt, k, a, b);
	rt = merge(a, merge(new_node(v), b));
}

int kth(int k)
{
	int a, b, c, d;
	split(rt, k - 1, a, b), split(b, 1, c, d);
	merge(a, merge(c, d));
	return tr[c].val;
}

int main()
{
	srand(time(0));
	scanf("%d", &n);
	for(register int i = 1; i <= n; ++i) scanf("%s", s[tot++]), insert(i - 1, tot - 1);
	scanf("%d", &m);
	for(register int i = 1, x; i <= m; ++i) scanf("%s", s[tot++]), insert(x = read(), tot - 1);
	scanf("%d", &q);
	for(register int i = 1, x; i <= q; ++i) x = read(), printf("%s\n", s[kth(x + 1)]);
}
posted @ 2020-12-01 13:45  leiyuanze  阅读(90)  评论(0编辑  收藏  举报