luogu 模板 P3391 【模板】文艺平衡树(Splay)。
知识点:1.splay模板题,练习splay,rotate顺序:x变成z的儿子,x的一个儿子变为y的一个儿子(具体哪个看代码),y变为x的儿子。
2.splay函数:如起码还需转两次,如最近两次旋转路径为直线,则先转x的父亲,再转x;如为折线,转两次x。
3.insert函数:记得记录父亲节点,如果父亲存在还要让父亲连向被插入数x,记得新建节点,新建节点两个儿子设置为0,将siz数设为1。
#include <bits/stdc++.h> using namespace std; int n,m; int cnt = 0; struct edge { int ch[2]; int ff; int siz; int val; int lazy; }t[200002]; int rt = 0; void pushup(int x) { //错误1:忘记加1(这个点本身),原为t[x].siz = t[t[x].ch[0]].siz + t[t[x].ch[1]].siz; t[x].siz = t[t[x].ch[0]].siz + t[t[x].ch[1]].siz + 1; } void pushdown(int x) { if(t[x].lazy) { t[t[x].ch[0]].lazy^=1; t[t[x].ch[1]].lazy^=1; t[x].lazy=0; swap(t[x].ch[0],t[x].ch[1]); } } void rotate(int x) { int y = t[x].ff; int z = t[y].ff; int k = t[y].ch[1] == x; t[z].ch[t[z].ch[1] == y] = x; t[x].ff = z; t[y].ch[k] = t[x].ch[k^1]; t[t[x].ch[k^1]].ff = y; t[x].ch[k^1] = y; t[y].ff = x; pushup(y); pushup(x); } void splay(int x,int goal) { while(t[x].ff != goal) { int y = t[x].ff,z = t[y].ff; if(z != goal) if((t[z].ch[1] == y)^(t[y].ch[1] == x))rotate(x); else rotate(y); rotate(x); } if(goal == 0)rt = x; } void insert(int x) { int u = rt,ff = 0; while(u) { ff = u; u = t[u].ch[t[u].val < x]; } u = ++cnt; if(ff)t[ff].ch[t[ff].val < x] = u; t[u].val = x; t[u].siz = 1; t[u].ff = ff; t[u].ch[0] = t[u].ch[1] = 0; splay(u,0); } int Kth(int k) { int u = rt; while(1) { //错误2:一开始把所有t[t[u].ch[0]].siz写成了t[u].siz,理解错误。 pushdown(u); if(t[t[u].ch[0]].siz >= k)u = t[u].ch[0]; else if(t[t[u].ch[0]].siz + 1 == k)return u; else k -= (t[t[u].ch[0]].siz + 1),u = t[u].ch[1]; } } void work(int l,int r) { int ls = Kth(l); int rs = Kth(r + 2); splay(ls,0); splay(rs,ls); t[t[t[rt].ch[1]].ch[0]].lazy ^= 1; } void print(int u) { pushdown(u); if(t[u].ch[0])print(t[u].ch[0]); if(t[u].val>1&&t[u].val<n+2)printf("%d ",t[u].val-1); if(t[u].ch[1])print(t[u].ch[1]); } int main() { scanf("%d%d",&n,&m); for(int i = 1;i <= n + 2;i++)insert(i);//这里是因为要留出两个空节点,一头一尾,给1当前驱,给n当后继,而1变成2,2变成3.....n变成n + 1。 int l,r; while(m--) { scanf("%d%d",&l,&r); work(l,r); } print(rt); return 0; }