[NOI2005]维护数列

[NOI2005]维护数列

题目描述

请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格)
这里写图片描述

输入格式:

输入文件的第 1 行包含两个数 N 和 M,N 表示初始时数列中数的个数,M 表示要进行的操作数目。 第 2 行包含 N 个数字,描述初始时的数列。 以下 M 行,每行一条命令,格式参见问题描述中的表格

输出格式:

对于输入数据中的 GET-SUM 和 MAX-SUM 操作,向输出文件依次打印结 果,每个答案(数字)占一行。

输入样例#1:

9 8 2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM

输出样例#1:

-1
10
1
10

说明:

你可以认为在任何时刻,数列中至少有 1 个数。
输入数据一定是正确的,即指定位置的数在数列中一定存在。
50%的数据中,任何时刻数列中最多含有 30 000 个数;
100%的数据中,任何时刻数列中最多含有 500 000 个数。
100%的数据中,任何时刻数列中任何一个数字均在[-1 000, 1 000]内。
100%的数据中,M ≤20 000,插入的数字总数不超过 4 000 000 。

题解:

splay模板题,码量巨大,纯属自虐。。。
对于1,2,5操作模板不用说,典型的序列区间处理。
对于操作3,只需要像线段树一样维护一个下放的标记就可以了,每次rotate的时候下放(这里注意由于有把序列置为0的操作,所以不能用标记记录改变的数,可以根据父亲的num来修改)。
对于操作4,也是维护一个标记,下放是转左右儿子,转lmax,rmax,把标记取反。
对于操作6,像线段树中的一样,维护一个max_sum,max_left,max_right,update时注意一下就可以了。
最后注意一下哨兵节点的初始化就可以了。

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

const int max_n = 4000001;
const int max_s = 11;
const int inf = 1e6+7;

struct node
{
    int num,sum,rank,rev,delta,max_sum,max_left,max_right,wh_ch;
    node *ch[2],*fa;

    void set_son(node *son,int wh);
    int get_wh();
    void update();
    void pushdown();
    void pushrev();
    void pushdelta(int now);
}pool[max_n],*null,*root,*temp,*bef,*beh;

inline int node::get_wh()
{
    return fa->ch[0]==this ? 0 : 1;
}

inline void node::set_son(node *son,int wh)
{
    ch[wh]=son;
    if(son!=null) son->fa=this;
    update();
}

inline void node::pushrev()
{
    swap(ch[0],ch[1]);
    swap(max_left,max_right);
    rev^=1;
}

inline void node::pushdelta(int now)
{
    delta=now;
    num=delta;
    sum=delta*rank;
    max_left=max_right=max_sum=max(sum,now);
    wh_ch=1;
}

inline void node::update()
{
    sum=ch[0]->sum+num+ch[1]->sum;
    rank=ch[0]->rank+1+ch[1]->rank;
    max_left=max(max(ch[0]->max_left,ch[0]->sum+num),ch[0]->sum+num+ch[1]->max_left);
    max_right=max(max(ch[1]->max_right,ch[1]->sum+num),ch[1]->sum+num+ch[0]->max_right);
    max_sum=max(max(ch[0]->max_sum,ch[1]->max_sum),max(num,ch[0]->max_right+num));
    max_sum=max(max(max_sum,num+ch[1]->max_left),ch[0]->max_right+num+ch[1]->max_left);
}

inline void node::pushdown()
{
    if(rev)
    {
        if(ch[0]!=null) ch[0]->pushrev();
        if(ch[1]!=null) ch[1]->pushrev();
        rev=0; 
    }
    if(wh_ch)
    {
        if(ch[0]!=null) ch[0]->pushdelta(delta);
        if(ch[1]!=null) ch[1]->pushdelta(delta);
        delta=0; wh_ch=0;   
    }
    update();
}

char s[max_s];
int a[max_n];
int n,m,x,tot;

inline int read()
{
    int ans=0;char c;bool flag=false;
    while( (c=getchar())==' ' || c=='\n' || c=='\r');
    if(c=='-') flag=true;else ans=c-'0';
    while((c=getchar()) >='0' && c<='9') ans=ans*10+c-'0';
    return ans*(flag?-1:1);
} 

inline void init()
{
    null=pool;
    null->sum=0;
    null->rank=0;
    null->num=0;
    null->wh_ch=0;
    null->rev=null->delta=0;
    null->max_left=null->max_right=null->max_sum=-inf;
    null->fa=null->ch[0]=null->ch[1]=null;
    root=null;
}

inline node *newnode(int a)
{
    node *now=pool+ ++tot;
    now->sum=a;
    now->rank=1;
    now->num=a;
    now->wh_ch=0;
    now->rev=now->delta=0;
    now->max_left=now->max_right=now->max_sum=a;
    now->fa=now->ch[0]=now->ch[1]=null;
    return now;
}

inline void rotate(node *now)
{
    node *f=now->fa,*grand=now->fa->fa;
    if(grand!=null) grand->pushdown();
    f->pushdown(); now->pushdown(); 
    int wh=now->get_wh();
    f->set_son(now->ch[wh^1],wh);
    now->set_son(f,wh^1);
    now->fa=grand;
    if(grand!=null) grand->ch[grand->ch[0]==f ? 0 : 1]=now;
    f->update(); now->update();
    if(grand!=null) grand->update();
}

inline void splay(node *now,node *tar)
{
    for(; now->fa!=tar; rotate(now))
      if(now->fa->fa!=tar) 
        now->get_wh()==now->fa->get_wh() ? rotate(now->fa) : rotate(now);
    if(tar==null) root=now;

}

inline node *build(int l,int r)
{
    if(l>r) return null;
    int mid=(l+r)>>1;
    node *now=newnode(a[mid]);
    now->set_son(build(l,mid-1),0);
    now->set_son(build(mid+1,r),1);
    return now;
}

inline node *find(int k)
{
    node *now=root;
    while(now!=null)
    {
        now->pushdown();
        if(k==now->rank-now->ch[1]->rank) break;
        if(k<now->rank-now->ch[1]->rank) now=now->ch[0];
        else k-=now->rank-now->ch[1]->rank,now=now->ch[1];
    }
    return now;
}

inline void insert()
{
    int pos,len;
    pos=read();
    len=read();
    for(int i=1; i<=len; ++i)
      a[i]=read();
    temp=build(1,len);
    node *now=find(pos+1),*last=find(pos+2);
    splay(now,null); splay(last,now);
    last->set_son(temp,0);
    now->update();
}

inline void delet()
{
    int pos,len;
    pos=read(); len=read();
    node *now=find(pos),*maxn=find(pos+len+1);
    splay(now,null);
    splay(maxn,now); 
    maxn->set_son(null,0);
    root->update();
}

inline void make_same()
{
    int pos,len,x;
    scanf("%d%d%d",&pos,&len,&x);
    node *now=find(pos),*maxn=find(pos+len+1);
    splay(now,null); splay(maxn,now);
    root->ch[1]->ch[0]->pushdelta(x);
    root->ch[1]->update(); root->update();
}

inline void reverse()
{
    int pos,len;
    scanf("%d%d",&pos,&len);
    node *now=find(pos),*maxn=find(pos+len+1);
    splay(now,null); splay(maxn,now);
    root->ch[1]->ch[0]->pushrev();
    root->ch[1]->update(); root->update();
}

inline void get_sum()
{
    int pos,len;
    scanf("%d%d",&pos,&len);
    node *now=find(pos),*maxn=find(pos+len+1);
    splay(now,null); 
    splay(maxn,now);
    printf("%d\n",root->ch[1]->ch[0]->sum);
}

inline void max_sum()
{
    printf("%d\n",root->max_sum);
}

int main()
{
    freopen("seq2005.in","r",stdin);
    freopen("seq2005.out","w",stdout);
    scanf("%d%d",&n,&m);
    init();
    a[1]=a[n+2]=-inf; 
    for(int i=2; i<=n+1; ++i)
      a[i]=read();
    root=build(1,n+2);
    for(int i=1; i<=m; ++i)
    {
        scanf("%s",s);
        if(s[0]=='I') insert();
        if(s[0]=='D') delet();
        if(s[0]=='M' && s[2]=='K') make_same();
        if(s[0]=='R') reverse(); 
        if(s[0]=='G') get_sum();
        if(s[0]=='M' && s[2]=='X') max_sum();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值