BZOJ3223: Tyvj 1729 文艺平衡树(洛谷P3391)

292 篇文章 1 订阅
281 篇文章 1 订阅

Splay

BZOJ题目传送门
洛谷题目传送门

平衡树维护序列,翻转区间即相当于交换左右子树,打个Lazy-Tag就行,感觉比书架简单。。。

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100000
using namespace std;
struct node{
    int to[2],fa,size;
}t[N+5];
int n,m,nd,rt,id[N+5];
bool lazy[N+5];
inline char readc(){//读优
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline int _read(){
    int num=0; char ch=readc();
    while (!isdigit(ch)) ch=readc();
    while (isdigit(ch)) num=num*10+ch-48,ch=readc();
    return num;
}
void pushup(int x){ t[x].size=t[t[x].to[0]].size+t[t[x].to[1]].size+1; }
void pushdown(int x){//下传标记
    if (!lazy[x]) return;
    int l=t[x].to[0],r=t[x].to[1];
    swap(t[x].to[0],t[x].to[1]);//翻转左右子树
    lazy[l]^=1,lazy[r]^=1,lazy[x]=0;
}
void rtt(int x,int &w){
    int y=t[x].fa,z=t[y].fa,p=(t[y].to[0]!=x);
    if (y==w) w=x;
    else t[z].to[t[z].to[0]!=y]=x;
    t[x].fa=z,t[y].fa=x,t[t[x].to[p^1]].fa=y;
    t[y].to[p]=t[x].to[p^1],t[x].to[p^1]=y;
    pushup(y),pushup(x);
}
void splay(int x,int &w){
    while (x!=w){
        int y=t[x].fa,z=t[y].fa;
        if (y!=w)
            if (t[y].to[0]==x^t[z].to[0]==y) rtt(x,w);
            else rtt(y,w);
        rtt(x,w);
    }
}
void build(int l,int r,int fa){//建树
    if (l>r) return;
    if (l==r){
        t[id[l]].size=1,t[id[l]].fa=fa;
        t[fa].to[l>fa]=id[l]; return;
    }
    int mid=(l+r)>>1;
    build(l,mid-1,mid),build(mid+1,r,mid);
    t[id[mid]].fa=fa,pushup(mid);
    t[fa].to[mid>fa]=id[mid];
}
int srch(int x,int w){//查找第w大的数
    pushdown(x); int p=t[x].to[0];
    if (t[p].size+1==w) return x;
    if (t[p].size>=w) return srch(p,w);
    return srch(t[x].to[1],w-t[p].size-1);
}
void nsrt(int l,int r){//翻转
    int x=srch(rt,l),y=srch(rt,r+2);
    splay(x,rt),splay(y,t[x].to[1]);//分别把l-1,r+1伸展到根和右子树
    lazy[t[y].to[0]]^=1;//l~r这段区间对应的就是y的左子树
}
int main(){
    n=_read(),m=_read();
    for (int i=1;i<=n+2;i++) id[i]=++nd;//这道题的初始序列就是1~n。
    build(1,n+2,0),rt=(n+3)>>1;//这里要加两个虚拟节点拿来伸展
    while (m--){
        int l=_read(),r=_read();
        nsrt(l,r);
    }
    for (int i=2;i<=n+1;i++)
        printf("%d ",srch(rt,i)-1);//记得减1
    return printf("\n"),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值