Splay 區間反轉
阿新 • • 發佈:2018-10-01
pro 交換 putc push pan tdi esp 我們 tag
同樣的,我們以一道題來引入。
傳送門
這次的任務比較少,只要求進行區間反轉。區間反轉?
這個好像用啥都是O(n)的吧……(這次vector,set也救不了你了)
我們來使用splay解決這個問題。我們既然要反轉一段區間,那我們肯定要把這個區間弄到一個地方。我們想一下上次所講的刪除操作,我們把要刪除的數的前驅後繼都找了出來並且一個旋轉到根,一個到根的右兒子。我們思考一下發現,如果把這個區間第一個數的前一個數(l-1)旋轉到根,把區間最後一個數的後一個數(r+1)旋轉到根的右兒子,那麽現在根的右兒子的左子樹就是這個區間了!
然後我們就可以大力(劃死)操作這棵子樹,比如進行區間加減,區間翻轉。翻轉其實很簡單,我們只要打上一個標記,在下放標記的時候,我們把當前節點的左右子樹交換,把左右子樹的標記全部異或1,把這個點的標記清零即可。(和線段樹下放lazy標記非常像)
然後實際操作的時候,比如我們要翻轉區間2~4,我們不是真的去找這倆數在哪,因為我們要反轉的話其實和數的大小是無關的,和下標的大小是有關的。取而代之的,我們找到在這棵平衡樹中相對應的排名為2和排名為4的兩個數的編號是多少,之後我們對它們進行操作即可。
然後最後我們在輸出整棵樹的時候只要輸出其中序遍歷即可。
我們來看一下代碼吧!
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #definerep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(‘\n‘) #define pr pair<int,int> #define mp make_pair #define fi first #define sc second using namespace std; typedef long long ll; const int M = 100005; const int N = 10000005; const int INF = 1000000009; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) op = -1; ch = getchar(); } while(ch >=‘0‘ && ch <= ‘9‘) { ans *= 10; ans += ch - ‘0‘; ch = getchar(); } return ans * op; } struct node { int fa,ch[2],son,cnt,tag,val; }t[M<<2]; int n,m,data[M<<1],root,x,y,idx; bool get(int x) { return t[t[x].fa].ch[1] == x; } void pushup(int x) { t[x].son = t[t[x].ch[0]].son + t[t[x].ch[1]].son + 1;//題中無重復的數 } void pushdown(int x) { if(x && t[x].tag) { t[t[x].ch[0]].tag ^= 1,t[t[x].ch[1]].tag ^= 1; swap(t[x].ch[0],t[x].ch[1]); t[x].tag = 0; } } void rotate(int x) { int y = t[x].fa,z = t[y].fa,k = get(x); t[z].ch[t[z].ch[1] == y] = x,t[x].fa = z; t[y].ch[k] = t[x].ch[k^1],t[t[y].ch[k]].fa = y; t[x].ch[k^1] = y,t[y].fa = x; pushup(x),pushup(y); } void splay(int x,int goal) { while(t[x].fa != goal) { int y = t[x].fa,z = t[y].fa; if(z != goal) (t[y].ch[0] == x) ^ (t[z].ch[0] == y) ? rotate(x) : rotate(y); rotate(x); } if(goal == 0) root = x; } int rk(int x)//找排名 { int u = root; while(1) { pushdown(u); if(t[t[u].ch[0]].son >= x) u = t[u].ch[0]; else { x -= (t[t[u].ch[0]].son + 1); if(!x) return u; u = t[u].ch[1]; } } } int build(int f,int l,int r)//直接構造一棵完美的splay { if(l > r) return 0; int mid = (l+r) >> 1,u = ++idx; t[u].val = data[mid],t[u].fa = f;//註意一定是mid的值! t[u].ch[0] = build(u,l,mid-1); t[u].ch[1] = build(u,mid+1,r); pushup(u); return u; } void turn(int x,int y) { int a = rk(x), b = rk(y+2);//因為插入了正負INF,所以相對應都向後移了一位 splay(a,0),splay(b,a);//以下操作上面都說過 pushdown(root); int g = t[t[root].ch[1]].ch[0]; t[g].tag ^= 1; } void write(int x)//輸出中序遍歷 { pushdown(x); if(t[x].ch[0]) write(t[x].ch[0]); if(t[x].val != INF && t[x].val != -INF) printf("%d ",t[x].val); if(t[t[x].ch[1]].val) write(t[x].ch[1]); } int main() { n = read(),m = read(); rep(i,1,n) data[i+1] = i; data[1] = -INF,data[n+2] = INF;//防止出錯 root = build(0,1,n+2); rep(i,1,m) x = read(),y = read(),turn(x,y); write(root); return 0; }
Splay 區間反轉