P3391 【模板】文藝平衡樹
【模板】文藝平衡樹
題目描述
您需要寫一種資料結構(可參考題目標題),來維護一個有序數列。
其中需要提供以下操作:翻轉一個區間。
輸入格式
第一行兩個正整數 \(n,m\),表示序列長度與操作個數。序列中第 \(i\) 項初始為 \(i\)。
接下來 \(m\) 行,每行兩個正整數 \(l,r\),表示翻轉的區間。
輸出格式
輸出一行 \(n\) 個正整數,表示原始序列經過 \(m\) 次變換後的結果。
【資料範圍】
對於 \(100\%\) 的資料,$1 \le n, m \leq 100000 $,\(1 \le l \le r \le n\)。
感覺和模板平衡樹很像,唯一的區別就是這題不用按點權分裂,而是直接按節點個數分裂
每次交換操作就直接給區間打tag,然後交換左右兒子
最後統計的時候直接中序dfs就好了
Code:
#include<bits/stdc++.h>
#include<ctime>
const int N=1e5+5;
using namespace std;
struct Tree{
int val,tag,siz,pri,ls,rs;
}t[N<<2];
int n,m,cnt,rt;
int new_(int w=0)
{
int x=++cnt;
t[x].val=w,t[x].siz=1,t[x].pri=rand();
return x;
}
void upd(int x)
{
t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
}
void pushdown(int x)
{
if(!t[x].tag)return ;
t[x].tag=0;
t[t[x].ls].tag^=1;
t[t[x].rs].tag^=1;
swap(t[x].ls,t[x].rs);
return ;
}
void split(int x,int k,int &a,int &b)
{
if(!x){a=b=0;return;}
pushdown(x);
if(k<=t[t[x].ls].siz)
{
b=x;
split(t[b].ls,k,a,t[b].ls);
upd(b);
}
else
{
a=x;k-=(t[t[x].ls].siz+1);
split(t[a].rs,k,t[a].rs,b);
upd(a);
}
}
int merge(int x,int y)
{
if(!x||!y)return x+y;
if(t[x].pri<t[y].pri)
{
pushdown(x);
t[x].rs=merge(t[x].rs,y);
upd(x);
return x;
}
else
{
pushdown(y);
t[y].ls=merge(x,t[y].ls);
upd(y);
return y;
}
}
queue<int> Q;
void calc(int x)
{
if(!x)return ;
pushdown(x);
calc(t[x].ls);
Q.push(t[x].val);
calc(t[x].rs);
}
void work()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
rt=merge(rt,new_(i));
}
for(int i=1,l,r;i<=m;i++)
{
scanf("%d%d",&l,&r);
int a=0,b=0,c=0;
split(rt,l-1,a,b);//a[1,l-1] : b[l,n]
split(b,r-l+1,b,c);//b[l,r] : c[r+1,n]
//cout<<"val:"<<t[a].val<<" "<<t[b].val<<" "<<t[c].val<<"\n";
//cout<<"siz:"<<t[a].siz<<" "<<t[b].siz<<" "<<t[c].siz<<"\n";
t[b].tag^=1;
rt=merge(merge(a,b),c);
}
calc(rt);
while(!Q.empty())
{
printf("%d ",Q.front());
Q.pop();
}
}
int main()
{
//freopen("P3391.in","r",stdin);
srand(clock());
work();
}