1. 程式人生 > >區間翻轉 bzoj 3223 文藝平衡樹 (splay)

區間翻轉 bzoj 3223 文藝平衡樹 (splay)

【bzoj3223】Tyvj 1729 文藝平衡樹

Description
您需要寫一種資料結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:翻轉一個區間,例如原有序序列是5 4 3 2 1,翻轉區間是[2,4]的話,結果是5 2 3 4 1

Input
第一行為n,m n表示初始序列有n個數,這個序列依次是(1,2……n-1,n) m表示翻轉操作次數 接下來m行每行兩個數[l,r] 資料保證 1<=l<=r<=n

Output
輸出一行n個數字,表示原始序列經過m次變換後的結果

Sample Input
5 3
1 3
1 3
1 4

Sample Output
4 3 2 1 5

HINT
N,M<=100000

思路
區間翻轉問題
翻轉區間->交換BST的左右子樹(逐層交換,正確性請自行驗證),區間標記降低翻轉次數
尋找要操作的區間[l,r]:將當前排名(size)為l的節點(節點l-1)轉到根,將當前排名為r+2的節點(節點r+1)轉到根的右子樹的根節點,則根的右子樹的根節點的左子樹為所求區間,直接標記該區間就可以了。(類似線段樹區間修改)
splay操作維護一棵較優的樹,三種方式進行旋轉操作。

注意
1.標記是在每一次訪問到一個新的節點是就要pushdown的(改變樹的結構會破壞標記區間,所以先一步下傳標記)
2.區分一個節點的排名和這個節點的值:這個節點的排名是它是當前陣列中的第幾個,用左兒子的size+1表示;這個節點的值是題目中輸入的數字,在本題中是1~n
3.增加數字為0和n+1的兩個哨兵節點,因為如果對區間1~x或x~n操作,用到前後的節點就需要0和n+1。
4.有些讀者會疑惑,難道交換左右子樹不會破壞BST的性質嗎?這就是容易混淆的一點,我們的區間操作是根據下標翻轉的,用陣列實現時下標就是陣列地址,子樹交換時,只是儲存內容的改變,下標位置(樹的形狀)只會在旋轉時改變,保證BST性質。

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <cstdlib>  
using namespace std;  

const int MAXN = 100010;
const int INF = 0x7fffffff;

#define lson tr[x].ch[0]  
#define rson tr[x].ch[1]

struct Node{  
    int ch[2], fa, v, size;  
    bool rv;//是否翻轉,因為偶數次翻轉無效,所以可以用bool,位運算 
void set(int x){v=x; ch[0]=ch[1]=fa=rv=0; size=1;} }tr[MAXN]; int n, m, root, tot, l, r, x1, x2; void updata(int x){//維護size if( !x ) return; tr[x].size = 1 + tr[lson].size + tr[rson].size; } void pushdown(int x){//打標記必備 if( !x ) return; if( tr[x].rv ){//x的子節點集合為翻轉區間 tr[lson].rv ^= 1; tr[rson].rv ^= 1;//標記下傳 tr[x].rv = 0;//消除 int t = lson; lson = rson; rson = t;//當前層翻轉 } } inline bool son(int x) {return tr[tr[x].fa].ch[1] == x;}//判斷ls,rs void link(int x, int y, bool cc){//建立x與y的新關係 tr[x].ch[cc] = y; tr[y].fa = x; } void rotate(int x){//一種重新連邊似的旋轉方式 int fa = tr[x].fa, ffa = tr[fa].fa, tt = son(x); link(ffa,x,son(fa)); link(fa,tr[x].ch[!tt],tt); link(x,fa,!tt); updata(fa); } void splay(int x, int f){ pushdown(x); if(f == 0) root = x;//l-1到root while(tr[x].fa != f){//到目標位置 if (tr[tr[x].fa].fa == f) {rotate(x); break;}//離目標位置只有一步 if (son(x) == son(tr[x].fa)) {rotate(tr[x].fa); rotate(x);}//與fa形成一條鏈(鏈狀是一種不優的狀態,所以通過先旋fa再旋x的方式降深度) else {rotate(x); rotate(x);}//Z字形(由於旋上去自動就會降深度,所以就是普通旋轉) } updata(x); } int build(int l, int r){ if(l>r) return 0; int x = ++tot; int mid = (l+r) >> 1; tr[x].set(mid); lson = build(l, mid-1); rson = build(mid+1, r); tr[lson].fa = tr[rson].fa = x; updata(x); return x; } int find(int x, int y){ pushdown(x);//有標記就必須pushdown,且寫成遞迴的形式 if (tr[lson].size+1 < y) return find(rson, y-tr[lson].size-1); else if(tr[lson].size+1 == y) return x; else return find(lson, y); } void dfs(int x){ pushdown(x);//標記在每一次訪問之前都要pushdown if(tr[x].ch[0]) dfs(tr[x].ch[0]); if(tr[x].v<=n && tr[x].v>=1) cout<<tr[x].v<<' '; if(tr[x].ch[1]) dfs(tr[x].ch[1]); } int main(){ scanf("%d%d", &n, &m); tot = root = 0; tr[0].v = tr[n+1].v = INF; root = build(0, n+1);//0和n+1哨兵節點 while(m--){ scanf("%d%d", &l, &r); x1 = find(root, l); x2 = find(root, r+2);//找到l-1,r+1的下標(因為有一個邊界0,所以第l個數其實是l-1,第r+2個數是r-1) splay(x1,0); splay(x2,x1); updata(root);//旋轉l-1到root,r+1到root->rs,標記位置為root->rs->ls此位置及其以下的內容就是l~r tr[tr[x2].ch[0]].rv ^= 1; updata(tr[x2].ch[0]); updata(x2); updata(root); } dfs(root);//中序遍歷 return 0; }

相關推薦

區間翻轉 bzoj 3223 文藝平衡 splay

【bzoj3223】Tyvj 1729 文藝平衡樹 Description 您需要寫一種資料結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:翻轉一個區間,例如原有序序列是5 4 3 2 1,翻轉區間是[2,4]的話,結果是5 2 3 4 1

P3391 【模板】文藝平衡Splay

spa 標題 -s gets 需要 () 序列 代碼 輸入 題目背景 這是一道經典的Splay模板題——文藝平衡樹。 題目描述 您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:翻轉一個區間,例如原有序序列是5 4 3 2 1,翻轉區間是[2

洛谷 P3391 【模板】文藝平衡Splay

模板 class opera void 直接 曾經 維護 如果 spa 先記一發非旋treap,splay什麽的以後再說 基本就是正常的非旋treap維護序列加上一個flip標記,如果某個節點flip為true表示以它為根的子樹需要一次翻轉。類似線段樹lazytag 每

洛谷P3391 【模板】文藝平衡SplayFHQ Treap

and fine 背景 clas bad 例如 spa 個數 static 題目背景 這是一道經典的Splay模板題——文藝平衡樹。 題目描述 您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:翻轉一個區間,例如

洛谷:P3384 【模板】文藝平衡Splay

定位 描述 ever 論文 這樣的 紅黑樹 裏的 來源 分別是 原題地址:https://www.luogu.org/problemnew/show/P3391 題目簡述 您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作: 翻轉一個區間,例如

【模板】文藝平衡Splay

rev 有序 base blog 其中 OS return 操作 i++ 題目背景 這是一道經典的Splay模板題——文藝平衡樹。 題目描述 您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:翻轉一個區間,例如原有序序列是5 4 3 2 1,

luogu P3391 【模板】文藝平衡Splay

嘟嘟嘟 突然覺得splay挺有意思的…… 這道題只有一個任務:區間翻轉。 首先應該知道的是,splay和線段樹一樣,都可以打標記,然後走到每一個節點之前先下傳。 那怎麼打標記呢?還應該有“區間”的思想。 對於區間\([L, R]\),想辦法把這個區間所在的子樹提取出來,然後打個標記即可。 那怎麼提取呢?其

【洛谷】P3391 【【模板】文藝平衡Splay

個數 mes 區間 翻轉 kth rand() 中序 clu span 文藝平衡樹的Fhq-Treap(無旋treap)做法 與普通平衡樹不同的是,這裏平衡樹需要維護的是一個序列,怎麽辦呢? 題解 平衡樹的性質:無論哪個操作都不會改變樹的中序遍歷 所以,對於一個序列平衡

洛谷P3391 【模板】文藝平衡Splay

printf template rev 一次 我們 truct name 直接 etc \(fhq-treap\)實現。 我們旋轉的時候已\(r\)用\(size\)分一次,在左子樹裏用\(l-1\)再用\(size\)分一次,剩下的右子樹我們直接打個懶標記即可。 然後註意

[BZOJ]3223 文藝平衡 區間翻轉

3223: Tyvj 1729 文藝平衡樹 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 4861  Solved: 2851 [Submit][Status][Discuss] Description 您需要

bzoj3223: Tyvj 1729 文藝平衡splay翻轉操作

http swap 代碼 mes return while i++ tchar font www.cnblogs.com/shaokele/ bzoj3223: Tyvj 1729 文藝平衡樹   Time Limit: 10 Sec   Memory Limit: 1

Luogu P3391 文藝平衡Splay or FHQ Treap

namespace 坐標 fine pre || pri rank style git 這道題要求區間反轉。。。好東西。。 對於Splay:把l-1旋到根,把r+1旋到根的右兒子,這樣r+1的左兒子就是整個區間了,然後對這個區間打個tg 註意要插-Inf和Inf到樹裏面

luogu P3369 【模板】普通平衡splay

嘟嘟嘟 突然覺得splay挺有意思,唯一不足的是這幾天是一天一道,debug到崩潰。 做了幾道平衡樹基礎題後,對這題有莫名的自信,還算愉快的敲完了程式碼後,發現樣例都過不去,然後就陷入了無限的debug環節了……算了,傷心的事就別再提了。 說一下這題怎麼做: 1.插入 不說了 void insert(

BZOJ 3223 普通平衡 | 平衡模板

++ rank 權值線段樹 enter bool markdown 不存在 esp 旋轉 #include <cstdio> #include <cmath> #include <cstring> #include <algorit

3223. 文藝平衡平衡-splay

bsp ever tar ++i %d 變換 mar oid n+1 Description 您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:翻轉一個區間,例如原有序序列是5 4 3 2 1,翻轉區間是[2,4]的話,結果是5 2

bzoj 3224: Tyvj 1728 普通平衡 && loj 104 普通平衡 splay

break lov pri pac erase names std namespace uniq 題目鏈接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 思路: splay樹模板題: 推薦博客:h

Luogu P3391 【模板】文藝平衡FHQ-Treap

題意 給出一個長為$n$序列$[1,2,...,n]$,$m$次操作,每次指定一段區間$[l,r]$,將這段區間翻轉,求最終序列 題解 雖然標題是$Splay$,但是我要用$FHQ\ Treap$,考慮先將$[l,r]$這段區間$split$出來($k$即為這段區間) void split(int o

Luogu5055 【模板】可持久化文藝平衡fhq-treap

  注意下傳標記時也需要新建節點。空間開的儘量大。 #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #inclu

關於平衡Treap

getchar() ret lin ota += getch ans 前驅後繼 特殊情況 平衡樹是什麽? 其實平衡樹就是支持旋轉的二叉查找樹。 什麽是二叉查找樹呢? 其實就是滿足(左子樹(全部節點) < 根

小橙書閱讀指南——紅黑平衡2

mage 檢查 旋轉 img his 基本 查找 兩種 1.2 從標準二叉樹的極端情況我們推導出2-3樹這樣的數據結構具備自平衡的特性,但是要實現這個特性在算法上相當復雜。考慮在大部分情況下,對於檢索的指數級時間消費O(lgN)要求並不嚴格。因此,我們會看到如何將一顆標準的