生活随笔
收集整理的這篇文章主要介紹了
洛谷 - P3391 【模板】文艺平衡树(Splay-区间反转)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
題目鏈接:點(diǎn)擊查看
題目大意:給出一個(gè)初始時(shí) a[ i ] = i 的序列,給出 m 次操作,每次操作將區(qū)間 [ l , r ] 內(nèi)的數(shù)列反轉(zhuǎn),問最終的數(shù)列是什么
題目分析:Splay處理區(qū)間反轉(zhuǎn)問題的模板題,通過debug了一晚上這個(gè)題目讓我稍微明白了一點(diǎn)東西,首先在Splay上找到一段區(qū)間的方法是先把點(diǎn) l - 1 旋到根上,再把點(diǎn) r + 1 旋到根的右節(jié)點(diǎn)上,那么根的右節(jié)點(diǎn)的左子樹代表的就是這個(gè)區(qū)間了,其次就是在尋找節(jié)點(diǎn) l - 1 和 r + 1 時(shí),不能用普通的 find ,原理我還沒想明白,要用 K-th 的 find 才能不出錯(cuò),應(yīng)該是這個(gè)題目的每個(gè)節(jié)點(diǎn)都有l(wèi)azy標(biāo)記,每次find的時(shí)候都需要實(shí)時(shí)下傳lazy才能保證正確性,而 find 和 K-th 的 find 應(yīng)該就是在這里處理的不太一樣,總之先記住吧,就是在處理區(qū)間問題時(shí),用 K-th 的 find 來尋找相應(yīng)節(jié)點(diǎn)
最后的答案顯然就是中序遍歷的結(jié)果了,因?yàn)闈M足SBT的性質(zhì)
因?yàn)榇蛄薼azy標(biāo)記,所以每次翻轉(zhuǎn)區(qū)間的時(shí)間復(fù)雜度可以在 logn 完成,所以總復(fù)雜度均攤下來就是 nlogn 了
代碼:
?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;class Splay//存儲(chǔ)規(guī)則:小左大右,重復(fù)節(jié)點(diǎn)記錄
{#define root e[0].ch[1] //該樹的根節(jié)點(diǎn)private:class node{public:int lazy;int v,father;//節(jié)點(diǎn)值,父級(jí)節(jié)點(diǎn) int ch[2];//左孩子=0,右孩子=1int sum;//自己+自己下級(jí)有多少節(jié)點(diǎn)。在根節(jié)點(diǎn)為1。int recy;//記錄自己被重復(fù)了幾次};node e[N];//Splay樹主體int n,points;//使用存儲(chǔ)數(shù),元素?cái)?shù)void update(int x){e[x].sum=e[e[x].ch[0]].sum+e[e[x].ch[1]].sum+e[x].recy;}int identify(int x){return e[e[x].father].ch[0]==x?0:1;}void connect(int x,int f,int son)//連接函數(shù)。用法:connect(son,father,1/0){e[x].father=f;e[f].ch[son]=x;}//作用:使得x的father=f,f的son=x.void rotate(int x){int y=e[x].father;int mroot=e[y].father;int mrootson=identify(y);int yson=identify(x);int B=e[x].ch[yson^1];connect(B,y,yson);connect(y,x,(yson^1));connect(x,mroot,mrootson);update(y);update(x);}void splay(int at,int to)//將at位置的節(jié)點(diǎn)移動(dòng)到to位置{to=e[to].father;while(e[at].father!=to){int up=e[at].father;if(e[up].father==to) rotate(at);else if(identify(up)==identify(at)){rotate(up);rotate(at);}else{rotate(at);rotate(at);}}}int crepoint(int v,int father){n++;e[n].lazy=0;e[n].v=v;e[n].father=father;e[n].sum=e[n].recy=1;e[n].ch[0]=e[n].ch[1]=0;return n;}void destroy(int x)//pop后摧毀節(jié)點(diǎn) {e[x].v=e[x].ch[0]=e[x].ch[1]=e[x].sum=e[x].father=e[x].recy=0;if(x==n) n--;//最大限度優(yōu)化}public:void init(){points=n=root=0;e[root].v=e[root].father=e[root].sum=e[root].recy=e[root].ch[0]=e[root].ch[1]=0;}int getroot(){return root;}int find(int v)//用于外部的find調(diào)用{int now=root;while(true){pushdown(now);if(e[now].v==v){
// splay(now,root);return now;}int next=v<e[now].v?0:1;if(!e[now].ch[next]) return 0;now=e[now].ch[next];}}int build(int v)//內(nèi)部調(diào)用的插入函數(shù),沒有splay {points++;if(points==1)//特判無點(diǎn)狀態(tài) {root=n+1;crepoint(v,0);}else{int now=root;while(true)//向下找到一個(gè)空節(jié)點(diǎn) {e[now].sum++;//自己的下級(jí)肯定增加了一個(gè)節(jié)點(diǎn) if(v==e[now].v){e[now].recy++;return now;}int next=v<e[now].v?0:1;if(!e[now].ch[next]){crepoint(v,now);e[now].ch[next]=n;return n;}now=e[now].ch[next];}}return 0;}void push(int v)//插入元素時(shí),先添加節(jié)點(diǎn),再進(jìn)行伸展 {int add=build(v);splay(add,root);}void pop(int v)//刪除節(jié)點(diǎn) {int deal=find(v);if(!deal) return;points--;if(e[deal].recy>1){e[deal].recy--;e[deal].sum--;return;}if(!e[deal].ch[0]){root=e[deal].ch[1];e[root].father=0;}else{int lef=e[deal].ch[0];while(e[lef].ch[1]) lef=e[lef].ch[1];splay(lef,e[deal].ch[0]);int rig=e[deal].ch[1];connect(rig,lef,1);connect(lef,0,1);update(lef);}destroy(deal);}int rank(int v)//獲取值為v的元素在這棵樹里是第幾小 {int ans=0,now=root;while(true){if(e[now].v==v) {ans+=e[e[now].ch[0]].sum;splay(now,root);return ans+1;}if(now==0) return 0;if(v<e[now].v) now=e[now].ch[0];else{ans=ans+e[e[now].ch[0]].sum+e[now].recy;now=e[now].ch[1];}}return 0;}int atrank(int x)//獲取第x小的元素的值 {if(x>points) return -inf;int now=root;while(true){pushdown(now);int minused=e[now].sum-e[e[now].ch[1]].sum;if(x>e[e[now].ch[0]].sum&&x<=minused) break;if(x<minused) now=e[now].ch[0];else{x=x-minused;now=e[now].ch[1];}}
// splay(now,root);return now;}int upper(int v)//尋找該值對應(yīng)的一個(gè)最近的上界值 {int now=root;int result=inf;while(now){if(e[now].v>=v&&e[now].v<result) result=e[now].v;if(v<e[now].v) now=e[now].ch[0];else now=e[now].ch[1];}return result;}int lower(int v)//尋找該值對應(yīng)的一個(gè)最近的下界值 {int now=root;int result=-inf;while(now){if(e[now].v<=v&&e[now].v>result) result=e[now].v;if(v>e[now].v) now=e[now].ch[1];else now=e[now].ch[0];}return result;}void pushdown(int k){if(k&&e[k].lazy){e[e[k].ch[0]].lazy^=1;e[e[k].ch[1]].lazy^=1;swap(e[k].ch[0],e[k].ch[1]);e[k].lazy=0;}}void reverse(int l,int r){splay(atrank(l-1+1),root);splay(atrank(r+1+1),e[root].ch[1]);e[e[e[root].ch[1]].ch[0]].lazy^=1;}void dfs(int k,int limit){pushdown(k);if(e[k].ch[0])dfs(e[k].ch[0],limit);if(e[k].v>=1&&e[k].v<=limit)printf("%d ",e[k].v);if(e[k].ch[1])dfs(e[k].ch[1],limit);}#undef root
}tree;int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int n,m;scanf("%d%d",&n,&m);for(int i=0;i<=n+1;i++)tree.push(i);while(m--){int l,r;scanf("%d%d",&l,&r);tree.reverse(l,r);}tree.dfs(tree.getroot(),n);return 0;
}
?
總結(jié)
以上是生活随笔為你收集整理的洛谷 - P3391 【模板】文艺平衡树(Splay-区间反转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。