NOI 2005维护数列

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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

SOL:

我们发现这是一道fhq_treap的模板题。

我们首先要会fhq_treap。(splay也可以)

我们发现一颗树无论如何旋转(或者split后merge),其中序遍历总是不变的(这是区间操作的基础)

如果我们按照想要的中序遍历建树并且合并,我们一定能得到正确的区间,所以平衡树的区间操作是正确的。

这也是平衡树的两大建立方式之一:按中序遍历建树(另外一种是按权值建树……就是最常见的那种)

这样就涉及到一个没有出现的函数:build(建树)函数。

我们可以暴力建树:(以洛谷p3391 文艺平衡树为例)

Treap* build(int l,int r){
    if (l>r) return rr;//我们有#define rr NULL
    Treap *now=new Treap(0);//这里的值是可以改的,就是数组里的真实值
    now->son[0]=build(l,MID-1);//递归处理
    now->son[1]=build(MID+1,r);
    now->rub();//更新该节点的信息
    return now;
}

我们有2个懒标记 :flip,mark。

filp 是指指是不是被染成了同一种颜色,而mark标记是是否翻转。我们来看一下如何维护mark标记

void pushdown(Treap* x)
{   if (x && x->mark)
    {   x->mark=0;
        swap(x->son[0],x->son[1]);
        if (x->son[0]) x->son[0]->mark^=1;
        if (x->son[1]) x->son[1]->mark^=1;
    }
}

我们发现异常的简洁。这样我们就可以轻松的维护mark标记了。(这就是用平衡树的原因,线段树的话要维护的lazy标记便不是很方便(做法是维护一个一次函数作为lazy标记))

那么我们就只剩最后一个操作了:MAX—SUM,我们便可以维护一个区间的ls,rs,maxsum三个值:

ls是从左边起的极大序列 ,rs从右边起的极大序列 。maxsum是这个区间的最大值。

那么我们合并时,新区间的最大值只有2种情况:

1.最大值是两个子区间的最大值中的一个。

2.最大值是左区间的rs并上当前节点并上右区间的ls

那么我们就做完了。

不得不吐槽的是,由于常数和OJ评测速度的问题,我卡了2个小时的常,写出来了一些奇奇怪怪的东西:

正常版:(30MB,洛谷0.3S,本校OJ1.3S)(说多了都是泪)

#include<bits/stdc++.h>
#define N 500007
#define MAXN 4000007
#define inf 0x3f3f3f3f
#define MARICLE __attribute__((optimize("-O2")))
using namespace std;
#define stack rrsb
int n,a[N],point;
#define sight(c) (‘0‘<=c&&c<=‘9‘)
#define Sight(c) (‘A‘<=c&&c<=‘Z‘||c==‘-‘)
#define abs(x) ((x)>0?(x):(-x))
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define getchar nc
inline char nc() {
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
MARICLE inline void read(int &x) {
    static char c;
    static int b;
    for (b=1,c=getchar(); !sight(c); c=getchar()) if (c==‘-‘) b=-1;
    for (x=0; sight(c); c=getchar()) x=x*10+c-48;
    x*=b;
}
MARICLE inline void reads(char* op) {
    static char c;
    static int b;
    for (b=0,c=getchar(); !Sight(c); c=getchar());
    for (; Sight(c); c=getchar()) op[b++]=c;
}
struct Treap {
    Treap *ch[2];
    int val,key,size,sum,l,r,m;
    bool flip,mark;
    MARICLE Treap() {
        /*val=l=r=m=-inf;sum=0;size=0;mark=flip=0;key=rand();*/
    }
    MARICLE void re() {
        val=l=r=m=-inf;
        sum=0;
        size=0;
        mark=flip=0;
        key=rand();
    }
    MARICLE inline void update() {
        size=ch[0]->size+ch[1]->size+1;
        sum=val+ch[0]->sum+ch[1]->sum;
        l=max(ch[0]->l,max(ch[0]->sum+val,ch[0]->sum+val+ch[1]->l));
        r=max(ch[1]->r,max(val+ch[1]->sum,ch[0]->r+val+ch[1]->sum));
        m=max(ch[0]->m,max(max(0,ch[0]->r)+val+max(0,ch[1]->l),ch[1]->m));
    }
};
Treap *null=new Treap(),*root=null,*stack[N],*x,*last;
MARICLE inline void Maintain_flip(Treap *o) {
    if(o==null)return;
    o->flip^=1;
    swap(o->l,o->r);
}
MARICLE inline void Maintain_mark(Treap *o,int c) {
    if(o==null)return;
    o->val=c;
    o->sum=o->size*c;
    o->l=o->r=o->m=max(o->size*c,c);
    o->mark=1;
}
MARICLE inline void pushdown(Treap *o) {
    if(o==null)return;
    if(o->flip) {
        o->flip^=1;
        Maintain_flip(o->ch[0]);
        Maintain_flip(o->ch[1]);
        swap(o->ch[0],o->ch[1]);
    }
    if(o->mark) {
        Maintain_mark(o->ch[0],o->val);
        Maintain_mark(o->ch[1],o->val);
        o->mark=0;
    }
}
MARICLE inline Treap* newTreap(int val) {
    Treap *o=new Treap();
    o->ch[1]=o->ch[0]=null;
    o->key=rand();
    o->val=o->sum=val;
    o->size=1;
    o->flip=o->mark=0;
    o->m=o->l=o->r=val;
    return o;
}
MARICLE Treap *merge(Treap *a,Treap *b) {
    if(a==null)return b;
    if(b==null)return a;
    pushdown(a);
    pushdown(b);
    if(a->key < b->key) {
        a->ch[1]=merge(a->ch[1],b);
        a->update();
        return a;
    } else {
        b->ch[0]=merge(a,b->ch[0]);
        b->update();
        return b;
    }
}
MARICLE void split(Treap *now,int k,Treap* &x,Treap* &y) {
    if (now==null) {
        x=y=null;
        return;
    }
    pushdown(now);
    int cmp=now->ch[0]?now->ch[0]->size+1:1;
    if (k<cmp) y=now,split(y->ch[0],k,x,y->ch[0]);
    else x=now,split(x->ch[1],k-cmp,x->ch[1],y);
    now->update();
}
int pos,c;
MARICLE inline Treap *build() {
    int p=0;
    for(int i=1; i<=n; i++) {
        read(a[i]);
        x=newTreap(a[i]);
        last=null;
        while(p&&stack[p]->key > x->key) {
            stack[p]->update();
            last=stack[p];
            stack[p--]=null;
        }
        if(p)stack[p]->ch[1]=x;
        x->ch[0]=last;
        stack[++p]=x;
    }
    while(p)stack[p--]->update();
    return stack[1];
}
MARICLE void adjust(Treap *o) {
    if(o==null)return;
    if(o->ch[0]!=null)adjust(o->ch[0]);
    if(o->ch[1]!=null)adjust(o->ch[1]);
    delete o;
}
Treap* XX,*X,*Y,*Z;
MARICLE inline void insert() {
    read(pos);
    read(n);
    Treap *o=build();
    split(root,pos,X,Y);
    root=merge(merge(X,o),Y);
}
MARICLE inline void erase() {
    read(pos);
    read(n);
    split(root,pos-1,X,Y);
    split(Y,n,Y,Z);
    adjust(Y);
    root=merge(X,Z);
}
MARICLE inline void reverse() {
    read(pos);
    read(n);
    split(root,pos-1,X,Y);
    split(Y,n,Y,Z);
    Maintain_flip(Y);
    root=merge(merge(X,Y),Z);
}
MARICLE inline void make_same() {
    read(pos);
    read(n);
    read(c);
    split(root,pos-1,X,Y);
    split(Y,n,Y,Z);
    Maintain_mark(Y,c);
    root=merge(merge(X,Y),Z);
}
MARICLE inline int get_sum() {
    read(pos);
    read(n);
    if(n==0)return 0;
    split(root,pos-1,X,Y);
    split(Y,n,Y,Z);
    int ret=Y->sum;
    root=merge(merge(X,Y),Z);
    return ret;
}
int m;
MARICLE void write(int x) {
    if (x<10) {
        putchar(‘0‘+x);
        return;
    }
    write(x/10);
    putchar(‘0‘+x%10);
}
MARICLE inline void writeln(int x) {
    if (x<0) putchar(‘-‘);
    write(abs(x));
    putchar(‘\n‘);
}
MARICLE int main() {
    null->re();
    read(n);
    read(m);
    root=build();
    char opt[15];
    while(m--) {
        reads(opt);
// scanf("%s",opt);
        switch(opt[0]) {
            case ‘I‘:
                insert();
                break;
            case ‘D‘:
                erase();
                break;
            case ‘M‘:
                if (opt[2]!=‘K‘) writeln(root->m);
                else make_same();
                break;
            case ‘G‘:
                writeln(get_sum());
                break;
            case ‘R‘:
                reverse();
                break;
        }
    }
}

我注意到我校的OJ空间开了128MB,便选择了随机删点。外加惨无人道的压代码,就写成了这样:

#include<bits/stdc++.h>
#define N 500007
#define inf 0x3f3f3f3f
#define MARICLE __attribute__((optimize("-O2")))
#define MARICL __attribute__((optimize("-O3")))
using namespace std;
#define stack rrsb
int n,a[N],point,ret;char opt[15];int pos,c,p,m;
#define sight(c) (‘0‘<=c&&c<=‘9‘)
#define Sight(c) (‘A‘<=c&&c<=‘Z‘||c==‘-‘)
#define abs(x) ((x)>0?(x):(-x))
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define getchar nc
#define rand org
MARICL inline int org(){ static int x=5438;return x^=x<<13,x^=x>>17,x^=x<<5;}
MARICL inline char nc(){static char buf[500000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,500000,stdin),p1==p2)?EOF:*p1++;}
MARICL inline void read(int &x) {static char c; static int b;for (b=1,c=getchar();!sight(c);c=getchar()) if (c==‘-‘) b=-1;for (x=0;sight(c);c=getchar()) x=x*10+c-48;x*=b;}
MARICL inline void reads(char* op) {static char c; static int b; for (b=0,c=getchar();!Sight(c);c=getchar());for (;Sight(c);c=getchar()) op[b++]=c;}
struct Treap{ Treap *ch[2];int val,key,size,sum,l,r,m;bool flip,mark;MARICL Treap(){}MARICL void re() {val=l=r=m=-inf;sum=0;size=0;mark=flip=0;key=rand();}MARICL inline void update(){ size=ch[0]->size+ch[1]->size+1;sum=val+ch[0]->sum+ch[1]->sum;l=max(ch[0]->l,max(ch[0]->sum+val,ch[0]->sum+val+ch[1]->l));r=max(ch[1]->r,max(val+ch[1]->sum,ch[0]->r+val+ch[1]->sum));m=max(ch[0]->m,max(max(0,ch[0]->r)+val+max(0,ch[1]->l),ch[1]->m));}};
Treap *null=new Treap(),*root=null,*stack[N],*x,*last;Treap* XX,*X,*Y,*Z;
MARICL inline void Maintain_flip(Treap *o){ if(o==null)return;o->flip^=1;swap(o->l,o->r);}
MARICL inline void Maintain_mark(Treap *o,int c){if(o==null)return;o->val=c;o->sum=o->size*c;o->l=o->r=o->m=max(o->size*c,c);o->mark=1;}
MARICL inline void pushdown(Treap *o){if(o==null)return;if(o->flip) {o->flip^=1; Maintain_flip(o->ch[0]); Maintain_flip(o->ch[1]); swap(o->ch[0],o->ch[1]);}if(o->mark) { Maintain_mark(o->ch[0],o->val); Maintain_mark(o->ch[1],o->val);o->mark=0;}}
MARICL inline Treap* newTreap(int val){Treap *o=new Treap(); o->ch[1]=o->ch[0]=null;o->key=rand(); o->val=o->sum=val;o->size=1;o->flip=o->mark=0;o->m=o->l=o->r=val;return o;}
MARICLE Treap *merge(Treap *a,Treap *b){ if(a==null)return b; if(b==null)return a;pushdown(a);pushdown(b);if(a->key < b->key) {a->ch[1]=merge(a->ch[1],b);a->update();return a;}else {b->ch[0]=merge(a,b->ch[0]);b->update();return b;}}
MARICL void split(Treap *now,int k,Treap* &x,Treap* &y){if (now==null) {x=y=null;return;}pushdown(now);int cmp=now->ch[0]?now->ch[0]->size+1:1;if (k<cmp) y=now,split(y->ch[0],k,x,y->ch[0]);else x=now,split(x->ch[1],k-cmp,x->ch[1],y);now->update();}
MARICL inline Treap *build(){for(int i=1;i<=n;i++){read(a[i]);x=newTreap(a[i]);last=null;while(p&&stack[p]->key > x->key){stack[p]->update();last=stack[p];stack[p--]=null;}if(p)stack[p]->ch[1]=x;x->ch[0]=last;stack[++p]=x;}while(p)stack[p--]->update();return stack[1];}
MARICL void adjust(Treap *o){if(o==null)return;if (!(rand()&15))return;if(o->ch[0]!=null)adjust(o->ch[0]);if(o->ch[1]!=null)adjust(o->ch[1]);delete o;}
MARICL inline void insert(){read(pos); read(n);Treap *o=build();split(root,pos,X,Y);root=merge(merge(X,o),Y);}
MARICL inline void erase(){ read(pos); read(n);split(root,pos-1,X,Y);split(Y,n,Y,Z);adjust(Y);root=merge(X,Z);}
MARICL inline void reverse(){ read(pos); read(n);split(root,pos-1,X,Y);split(Y,n,Y,Z);Maintain_flip(Y);root=merge(merge(X,Y),Z);}
MARICL inline void make_same(){read(pos); read(n); read(c);split(root,pos-1,X,Y);split(Y,n,Y,Z);Maintain_mark(Y,c);root=merge(merge(X,Y),Z);}
MARICL inline int get_sum(){ read(pos); read(n);if(n==0)return 0;split(root,pos-1,X,Y);split(Y,n,Y,Z);ret=Y->sum;root=merge(merge(X,Y),Z);return ret;}
MARICLE void write(int x){if (x<10) {putchar(‘0‘+x);return;} write(x/10); putchar(‘0‘+x%10);}
MARICLE inline void writeln(int x){if (x<0) putchar(‘-‘); write(abs(x)); putchar(‘\n‘);}
MARICL int main(){null->re();read(n);read(m);root=build();while(m--){ reads(opt);switch(opt[0]){ case ‘I‘:insert();break;case ‘D‘:erase();break;case ‘M‘:if (opt[2]!=‘K‘) writeln(root->m); else make_same(); break;case ‘G‘:writeln(get_sum());break;case ‘R‘:reverse();break;}}}

代码丑不要打我。。。

时间: 2024-08-03 08:25:56

NOI 2005维护数列的相关文章

BZOJ 1500 NOI 2005 维修数列 Splay

题意:见下图 传说级别的NOI数据结构神题,像我这种弱渣花了一下午的时间才A掉,最后发现竟然是边界值的问题没处理好.. 这个题对Splay的所有操作基本是全了. 插入:新建一颗Splay Tree,然后把对应节点Splay到根的右儿子上,再把新建的树连上. 删除:把要删除的区间Splay到根的右儿子的左儿子上,递归free掉.(这里可以用数组优化,可以避免递归free节省时间) 修改,翻转:打标记,在需要的时候下传标记,和线段树差不多,翻转标记下传时,要将左右儿子的左右儿子分别交换. 求和:维护

bzoj 1500 [NOI 2005] 维修数列

题目大意不多说了 貌似每个苦逼的acmer都要做一下这个splay树的模版题目吧 还是有很多操作的,估计够以后当模版了.... 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 7 using namespace std; 8 const int N = 1000010; 9 cons

NOI 2005 维修数列

妈妈呀我终于过了!!!原来是数据坑我!!! 弃疗弃疗弃疗弃疗!!!!我调了一天呢....被GET_SUM 8 0打败了.... 啥也不说了....还是我太年轻.... 1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<queue> 6 #include<cstring> 7 #define PAU put

noi 2005维修数列

OI生涯中最想过的一题=v= splay加上各种各样的操作,样例又不敢拿来调了... 感觉指针写splay好难调,所以以后写splay都用数组吧(其实是因为黄大神也用数组orz) 好像是2小时敲完..debug了2天TAT一开始把操作6和某提混了... 然后搞来搞去才发现读入有个毛病0A0 再然后又发现一直以来自己的pushdown和别人是不一样的..(那些题我居然过了...) 最后被内存限制卡住,时间换空间..(然而本地测试90...难道是我的电脑太慢?) 虽然很累,但学了不少,值了 1 #i

【NOI2005】维护数列

描述 请写一个程序,要求维护一个数列,支持以下6种操作:(请注意,格式栏中的下划线' _ '表示实际输入文件中的空格) 格式 输入格式 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一条命令,格式参见问题描述中的表格.任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内.插入的数字总数不超过4 000 000个,输入文件大小不超过20MB

bzoj1789 AHOI 维护数列(线段树)

首先想到线段树,然后刚开始写忽然想到树状数组求和岂不是更快,而且编程复杂度又小,于是把之前写的删掉,写树状数组,写完模版之后忽然发现这题竟然是区间修改! 于是又删掉重写,忽然发现不会处理又加又乘的,果断看题解-- 经过几乎两个小时的调试,终于1A. 需要注意的是,一定要让线段树的每一个区间保存的值时刻为正确的,这样才能在调用时直接返回.比如说这道题的change和query操作的最后一句话: sum:=f(g[k<<1]+g[k<<1+1]) 而不是 sum:=f(t[k<&

NOI2005维护数列

1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 6263  Solved: 1879[Submit][Status] Description Input 输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一条命令,格式参见问题描述中的表格. Output 对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印

数据结构(Splay平衡树):COGS 339. [NOI2005] 维护数列

339. [NOI2005] 维护数列 ★★★★☆   输入文件:seq2005.in   输出文件:seq2005.out   简单对比 时间限制:3 s   内存限制:256 MB [问题描述] 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格) 操作编号 输入文件中的格式 说明 1.  插入 INSERT_posi_tot_c1_c2_..._ctot 在当前数列的第 posi 个数字后插入 tot 个数字:c1, c2,

☆ [ZJOI2006] 书架 「平衡树维护数列」

题目类型:平衡树 传送门:>Here< 题意:要求维护一个数列,支持:将某个元素置顶或置底,交换某元素与其前驱或后继的位置,查询编号为\(S\)的元素的排名,查询排名第\(k\)的元素编号 解题思路 可以说是平衡树维护数列的入门题.当平衡树在维护数列时,关键字是在数列中的排名.因此中序遍历即为当前数列.注意在平衡树维护数列中,会涉及到两个编号.一个编号是这个节点在平衡树内的编号,一般外界是不会直接访问的.另一个是题目赋予的编号,代表这个位置数列上对应的值.外部编号即为一个附加值.当我们需要找到