POJ3580 splay

题意:对于一个序列有若干的操作 分别为插入、删除 单个数,翻转序列,使中间一段序列进行指定的循环移位、

查询区间最小值


解法:对于splay来说插入、删除、翻转已经是很简单的操作了,查询最小值更是比线段树还简单

,这一道题目的难点就在于循环移位 

这里就有一个绝妙的解释办法:

 假设我们在区间[l,r]要循环移位的数为k位

那么最后等价的结果是[r-k+1,r][l,l+k-1]

现在我们把它倒过来看就是 [l+k-1,l][r,r-k+1]这其实是两个区间各自的翻转!

那么我们可以把循环移位分成

翻转区间[l,l+k-1] 翻转区间 [r-k+1,r] 最后翻转整个区间即可

#include<stdio.h>
#include<string.h>
#include<limits.h>
#include<iostream>
using namespace std;
#define ls ch[rt][0]
#define rs ch[rt][1]
#define rls ch[root][0]
#define rrs ch[root][1]
#define inf ~0U>>2
#define maxn 222222
int fa[maxn],ch[maxn][2],num[maxn],mi[maxn],add[maxn],va[maxn],rev[maxn];
int cnt,root;
inline void down(int rt){
    if(rev[rt]){
        swap(ls,rs);
        if(ls)rev[ls]^=1;
        if(rs)rev[rs]^=1;
        rev[rt]=0;
    }if(add[rt]){
        int w=add[rt];
        if(ls)add[ls]+=w,mi[ls]+=w,va[ls]+=w;
        if(rs)add[rs]+=w,mi[rs]+=w,va[rs]+=w;
        add[rt]=0;
    }
}
inline void up(int rt){
    mi[rt]=inf;
    mi[rt]=min(min(mi[ls],mi[rs]),va[rt]);
    num[rt]=num[ls]+num[rs]+1;
}
inline void rot(int rt){
    int f=fa[rt],side=ch[f][1]==rt,&ll=ch[rt][!side];
    fa[ll]=f,ch[f][side]=ll;
    fa[rt]=fa[f],ch[fa[f]][ch[fa[f]][1]==f]=rt;
    fa[f]=rt,ch[rt][!side]=f;
    up(f),up(rt);
}
inline void splay(int rt,int aim){
    while(fa[rt]!=aim){
        int f=fa[rt],ff=fa[f];
        if(ff==aim)rot(rt);
        else if((ch[ff][1]==f)==(ch[f][1]==rt))rot(f),rot(rt);
        else rot(rt),rot(rt);
    }if(!aim)root=rt;
}
inline void find(int sub,int tot){
    int rt=sub;
    while(1){
        down(rt);
        if(num[ls]==tot-1)break;
        if(num[ls]>=tot)rt=ls;
        else tot-=num[ls]+1,rt=rs;
    }
    splay(rt,fa[sub]);
}
void _add(int l,int r,int w){
    find(root,l);
    find(rrs,r-l+2);
    int rt=ch[rrs][0];
    add[rt]+=w,mi[rt]+=w,va[rt]+=w;
    up(fa[rt]);up(root);
}
int query(int l,int r){
    find(root,l);
    find(rrs,r-l+2);
    return mi[ch[rrs][0]];
}
void ins(int l,int w){
    find(root,l+1);
    down(root);
    fa[rrs]=++cnt,ch[cnt][1]=rrs,ch[cnt][0]=0;
    fa[cnt]=root,rrs=cnt;
    va[cnt]=w;
    up(cnt),up(root);
}
void del(int l){
    find(root,l+1),find(rrs,1);
    down(root);
    fa[rls]=rrs,ch[rrs][0]=rls;
    fa[rrs]=0,root=rrs;
    up(root);
}
void flip(int l,int r){
    find(root,l),find(rrs,r-l+2);
    rev[ch[rrs][0]]^=1;
}
void wheel(int l,int r,int tt){
    if(!tt)return ;
    int t=(tt%(r-l+1)+(r-l+1))%(r-l+1);
    if(t==r)return ;
    flip(l,r-t);
    flip(r-t+1,r);
    flip(l,r);
}
int n,m,a,b,c;
char op[11];
int main()
{
    scanf("%d",&n);
    
    va[0]=mi[0]=va[1]=mi[1]=inf;
    root=cnt=num[1]=1;
    while(n--){
        scanf("%d",&a);
        va[++cnt]=a;num[cnt]=cnt;
        ch[cnt][0]=cnt-1,fa[cnt-1]=cnt;
        mi[cnt]=min(a,mi[cnt-1]);
    }
    root=cnt;
    va[++cnt]=inf,num[cnt]=1;
    ch[cnt-1][1]=cnt,fa[cnt]=cnt-1;mi[cnt]=inf;
    num[root]=cnt;
    scanf("%d",&m);
    
    while(m--){
        scanf("%s%d",op,&a);
        if(op[0]=='A')scanf("%d%d",&b,&c),_add(a,b,c);
        else if(op[0]=='I')scanf("%d",&b),ins(a,b);
        else if(op[0]=='D')del(a);
        else if(op[0]=='M')scanf("%d",&b),printf("%d\n",query(a,b));
        else if(op[3]=='E')scanf("%d",&b),flip(a,b);
        else scanf("%d%d",&b,&c),wheel(a,b,c);
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值