[LGOJ1503]鬼子进村——[fhq treap]

【题目背景】

小卡正在新家的客厅中看电视。电视里正在播放放了千八百次依旧重播的《亮剑》,剧中李云龙带领的独立团在一个县城遇到了一个鬼子小队,于是独立团与鬼子展开游击战。

【题目描述】

县城里有 n n n个用地道相连的房子,第 i i i个只与第 i − 1 i-1 i1和第 i + 1 i+1 i+1个相连。这时有 m m m个消息依次传来

  • 消息为 D    x D\;x Dx:鬼子将 x x x号房子摧毁了,地道被堵上。

  • 消息为 R R R :村民们将鬼子上一个摧毁的房子修复了。

  • 消息为 Q    x Q\;x Qx:有一名士兵被围堵在 x x x号房子中。

李云龙收到信息很紧张,他想知道每一个被围堵的士兵能够到达的房子有几个。

【输入格式】
第一行2个整数 n , m n,m nm(n,m<=50000)。

接下来 m m m行,有如题目所说的三种信息共 m m m条。

【输出格式】
对于每一个被围堵的士兵,输出该士兵能够到达的房子数。

S a m p l e    I n p u t Sample\;Input SampleInput

7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4

S a m p l e    O u t p u t Sample\;Output SampleOutput

1
0
2
4

【题意分析】
首先,读完题目,我笑了半天。
//tm《亮剑》都能编题目

这道题目很明显是平衡树,但是我们可以用一种特殊的平衡树:

# f h q    T r e a p fhq\;Treap fhqTreap 无旋Treap
//fhq大佬orz

如果你想学 S p l a y Splay Splay然后被 r o t a t e ( ) rotate() rotate()转晕了,那么可以兼容Splay所有功能的fhq Treap绝对是不二之选:

(https://www.luogu.org/blog/2662945155-minions/fhq-treap-xue-xi-bi-ji)
(https://blog.csdn.net/CABI_ZGX/article/details/79963427)

再回到本题,直接在Treap上删点插点比较麻烦,那么我们就反向思考一下:每次轰炸的时候,将被轰炸的节点放进Treap,修复的时候反而拿出来,至于询问则非常简单:找到这个点的前驱,后继,相减就是能到达的房子总数。

注意到修复操作有一个性质:将上一个被轰炸的房子修复,那么简单地维护一个栈,轰炸了就 S . p u s h ( ) S.push() S.push(),修复的时候取栈顶 S . t o p ( ) S.top() S.top(),然后 S . p o p ( ) S.pop() S.pop()弹出
如果被询问的点被轰炸了且没有修复,那么直接输出0。

//蒟蒻用stl自带的栈奇怪地RE,然后改了手写栈就对了。其实并没有什么区别
//为了维护Treap,要跟Splay一样插♂哨兵,但是插♂-INF和INF会错,后来看了下题解其实应该插♂节点0和节点n+1,不然你访问的东西会很奇怪(用了传参)

Code:

#include
#include
#include
#include
#include
#include
#define MAX 200000
#define INF 1 << 29
using namespace std;

struct fhq_Treap{
	int son[5];
	int size,v,rand;
}tree[MAX];   //Treap结构体

int root,n,q,cnt;
bool Destroyed[MAX];  //是否被轰炸
int stack[MAX],top;    //zz手写栈

inline int read (){
	int s = 0,w = 1;
	char ch = getchar ();
	while (!isdigit (ch)){if (ch == '-')w = -1;ch = getchar ();}
	while (isdigit (ch)){s = (s << 3)+(s << 1)+ch-'0';ch = getchar ();}
	return s*w;
}

inline void push_up (int x){
	if (!x)return;
	tree[x].size = tree[tree[x].son[0]].size+tree[tree[x].son[1]].size+1;
}   //跟Splay一样,向上更新子树大小


inline int rank(int x,int k){
	if (!x)return 0;  //如果没有了就退出
	if (k<=tree[x].v)return rank(tree[x].son[0],k);
	else return rank(tree[x].son[1],k)+tree[tree[x].son[0]].size+1;
}     //拿到Treap中的排名

inline void merge (int &x,int l,int r){  
    //合并,x是传参
	if (!l||!r){   //不存在qwq
		x = l+r;
		return;
	}
	if (tree[l].rand <= tree[r].rand){   //维护平衡树性质
		x = l;merge (tree[x].son[1],tree[x].son[1],r);
		push_up(x);return;
	}
	x = r;merge (tree[x].son[0],l,tree[x].son[0]);
	push_up(x);  //记得向上更新
}

inline void split (int x,int &l,int &r,int k){
    //分裂,l,r是传参
	if (!k){   //k=0是全部在右子树
		l = 0;r = x;
		return;
	}   
	if (k == tree[x].size){   //全部在左子树
		l = x;r = 0;
		return;
	}
	if (k > tree[tree[x].son[0]].size){
		l = x;split (tree[x].son[1],tree[x].son[1],r,k-tree[tree[x].son[0]].size-1);
		push_up(x);return;
	}
	r = x;split (tree[x].son[0],l,tree[x].son[0],k);
	push_up(x);
}

inline void new_node (int &x,int now){
	tree[x = ++cnt].rand = rand ();
	tree[x].v = now;
	tree[x].size = 1;
}   //建新节点

inline void insert (int now){
	int x,y,z;
	int rk = rank(root,now);
	split (root,x,y,rk);
	new_node (z,now);
	merge (x,x,z);
	merge (root,x,y);
}    //分开,插入,合并

inline int prev (int now){
	int x,y,z;
	int rk = rank (root,now);
	split (root,x,y,rk);
	split (x,x,z,rk-1);
	int ans = tree[z].v;
	merge (x,x,z);
	merge (root,x,y);
	return ans;
}      //找前驱

inline int succ (int now){
	int x,y,z;
	int rk = rank (root,now+1);
	split (root,x,y,rk+1);
	split (x,x,z,rk);
	int ans = tree[z].v;
	merge (x,x,z);
	merge (root,x,y);
	return ans;
}       //跟找前驱一样,找后继

inline void del (int now){
	int x,y,z;
	int rk = rank (root,now)+1;
	split (root,x,y,rk);
	split (x,x,z,rk-1);
	merge (root,x,y);
}    //删节点,分裂再合并

int main(){ 
    n = read (),q = read ();
    //insert (-INF);
    //insert (INF);
    //这样会错。。。
    insert (0);
    insert (n+1);
    while (q--){
    	//stack  S;
    	//这样也会错。。。
        char s[5];
        int x;
        scanf("%s",&s);
        if (s[0] == 'D'){
            x = read ();
            Destroyed[x]=true;
            insert (x);
            //S.push (x);
            stack[++top] = x;
            //再也不用stl的stack了,效率慢还报错
        }
        if (s[0] == 'R'){
            //x = S.top ();
            //S.pop ();
			x = stack[top--];
            del (x);
            Destroyed[x]=false;
        }
        if (s[0] == 'Q'){
            x = read ();
            if (Destroyed[x])puts("0");  
            //被炸得稀巴烂,还玩蛇皮
            else printf("%d\n",succ(x)-prev(x)-1);
            //后继减前驱,插了哨兵再减1
        }
    }
    return 0;
}

你可能感兴趣的:(平衡树,Treap,fhq,Treap)