【例题】【Splay】NKOJ2504 区间翻转问题

22 篇文章 0 订阅
4 篇文章 0 订阅

NKOJ2504 区间翻转问题
时间限制 : 10000 MS 空间限制 : 265000 KB

问题描述
给你一个长度为N的序列{ai}和M个操作
1.查询第k个数的值
2.将第k个数增加d
3.查询一段区间的和
4.查询一段区间的最大值
5.将一段区间镜面翻转(例如序列{1,2,3,4,5,6},将从2到5的区间翻转后得到序列{1,5,4,3,2,6})
对于除操作2,5以外的操作,输出相应的答案

输入格式
第一行两个正整数N,M
第二行N个整数,为初始的序列
第三行到底M+2行,每行若干个整数
·如果第一个数为1,那么后面一个正整数k,表示查询第k个数的值
·如果第一个数是2,那么后面两个正整数k,d,表示将ak增加d
·如果第一个数为3,那么后面两个正整数l,r,表示查询从al到ar的区间和
·如果第一个数为4,那么后面两个正整数l,r,表示查询从al到ar的最大值
·如果第一个数为5,那么后面两个正整数l,r,表示翻转从al到ar的这个区间

输出格式
除操作2,5外每个操作输出占一行,一个整数,为本次提问的答案

样例输入
6 8
1 2 3 4 5 6
1 4
3 2 5
4 2 2
5 2 5
3 1 3
5 2 5
2 5 1
4 1 6

样例输出
4
14
2
10
6

提示
2<=N<=100000
1<=M<=100000
原序列1<=ai<=1000
每次1<=k<=N,1<=l<=r<=N,1<=d<=1000

来源 感谢nodgd命题并提供数据

思路:
维护下标,将每个数的下标存入树中。
(1)、查询第k个数:直接找
(2)、第k个数增加d:直接加
(3)、维护区间和。操作时将x-1为根、y+1为右儿子,则y+1左儿子的sum即为所求
(4)、维护区间最大。操作同(3)
(5)、lazy标记。操作同(3),再将y+1左儿子打标记

(3)、(4)、(5)注意覆盖1或n的情况特殊处理

#include<cstdio>
#include<iostream>
using namespace std;
const int need=100005;

int a[need];
//.............................................
inline void in_(int &d)
{
    char t=getchar();bool mark=0;
    while(t<'0'||t>'9') {if(t=='-') mark=1;t=getchar();}
    for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';if(mark) d=-d;
}
inline void out_(int x)
{
    if(x<0) {x=-x;putchar('-');}
    if(x>=10) out_(x/10);
    putchar(x%10+'0');
}
//.............................................
int tot,root;
int fa[need],ls[need],rs[need],si[need],num[need],sum[need],mm[need];
bool lazy[need];

inline void putdown(int x)
{
    lazy[x]=0;
    swap(ls[x],rs[x]);
    if(ls[x]) lazy[ls[x]]^=1;
    if(rs[x]) lazy[rs[x]]^=1;
} 
inline void NBHB(int x)
{
    si[x]=si[ls[x]]+si[rs[x]]+1;
    sum[x]=sum[ls[x]]+sum[rs[x]]+a[num[x]];
    mm[x]=max(a[num[x]],max(mm[ls[x]],mm[rs[x]]));
}
inline void sr(int x)
{
    int y=fa[x],z=fa[y];
    if(lazy[z]) putdown(z);
    if(lazy[y]) putdown(y);
    if(lazy[x]) putdown(x);
    ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z;
    ls[y]=rs[x]; fa[rs[x]]=y;
    rs[x]=y; fa[y]=x;
    NBHB(y),NBHB(x);
}
inline void sl(int x)
{
    int y=fa[x],z=fa[y];
    if(lazy[z]) putdown(z);
    if(lazy[y]) putdown(y);
    if(lazy[x]) putdown(x);
    ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z;
    rs[y]=ls[x]; fa[ls[x]]=y;
    ls[x]=y; fa[y]=x;
    NBHB(y),NBHB(x);
}

int top,s[need];

void splay(int x)
{
    top=0;s[++top]=x;
    for(int i=x;fa[i];i=fa[i]) s[++top]=fa[i];
    while(top)
    {
        if(lazy[s[top]]) putdown(s[top]);
        top--;
    }
    int y;
    while(y=fa[x])
    {
        if(ls[y]==x) sr(x);
        else sl(x);
    }
    root=x;
}
void insert(int d) //不用putdown,因为先建树,再操作
{
    if(tot==0)
    {
        num[root=tot=1]=d;
        sum[1]=mm[1]=a[d];
        si[1]=1;
        ls[1]=rs[1]=fa[1]=0;
        return ;
    }
    tot++;
    int p=root;
    while(true)
    {
        si[p]++;
        sum[p]+=a[d];
        mm[p]=max(mm[p],a[d]);
        if(d<num[p]) 
         if(ls[p]) p=ls[p];
         else{ls[p]=tot;break;}
        else 
         if(rs[p]) p=rs[p];
         else{rs[p]=tot;break;}
    }
    si[tot]=1,sum[tot]=a[tot],fa[tot]=p,mm[tot]=a[d],num[tot]=d;
    splay(tot);
}
//.............................................
int k,d,l,r;

int getk(int k)
{
    int p=root;
    while(true)
    {
        if(lazy[p]) putdown(p);
        if(ls[p]&&si[ls[p]]>=k) p=ls[p];
        else if(k==1||(ls[p]&&si[ls[p]]==k-1)) return p;
        else {k-=si[ls[p]]+1;p=rs[p];}
    }
}
void change(int xx)
{
    int y;
    while(fa[xx]!=root)
    {
        y=fa[xx];
        if(lazy[y]) putdown(y);
        if(lazy[xx]) putdown(xx);
        if(ls[y]==xx) sr(xx);
        else sl(xx);
    }
}
int get()
{
    if(l>1&&r<tot)
    {
        int a=getk(l-1),b=getk(r+1);
        splay(a);
        change(b);
        return ls[b];
    }
    else if(l>1&&r==tot)
    {
        int a=getk(l-1);
        splay(a);
        return rs[a];
    }
    else if(l==1&&r<tot)
    {
        int b=getk(r+1);
        splay(b);
        return ls[b];
    }
    else return root;
}
//.............................................

int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) in_(a[i]),insert(i);
    for(int i=1,b,kk;i<=m;i++)
    {
        in_(b);
        if(b==1)
        {
            in_(k);
            out_(a[num[getk(k)]]),putchar(10);
        }
        else if(b==2)
        {
            in_(k),in_(b);
            kk=num[getk(k)];
            a[kk]+=b;
            splay(kk);
        }
        else
        {
            in_(l),in_(r);
            kk=get();
            if(b==3) out_(sum[kk]),putchar(10);
            else if(b==4) out_(mm[kk]),putchar(10);
            else lazy[kk]^=1;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值