刷题总结——维护数列(NOI2005 bzoj1500 splay)

题目:

题目背景

NOI2005 DAY1 T2

题目描述

请写一个程序,要求维护一个数列,支持以下 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 
10 

10

备注

【样例说明】
初始时,我们拥有数列:
2 -6 3 5 1 -5 -3 6 3

执行操作 GET-SUM 5 4 ,表示求出数列中从第 5 个数开始连续 4 个数字之和,如下图中的灰色部分 1+(-5)+(-3)+6 = -1:
2 -6 3 5 1 -5 -3 6 3

执行操作 MAX-SUM ,表示要求求出当前数列中最大的一段和,即如下图所示,应为 3+5+1+(-5)+(-3)+6+3 = 10:
2 -6 3 5 1 -5 -3 6 3

执行操作 INSERT 8 3 -5 7 2,即在数列中第 8 个数字后插入 -5 7 2,如下所示的灰色部分:
2 -6 3 5 1 -5 -3 6 -5 7 2 3

执行操作 DELETE 12 1,表示删除第 12 个数字,即最后一个:
2 -6 3 5 1 -5 -3 6 -5 7 2

执行操作 MAKE-SAME 3 3 2 ,表示从第 3 个数开始的 3 个数字,即下图中的灰色部分,统一修改为 2 :
2 -6 3 5 1 -5 -3 6 -5 7 2
    改为
2 -6 2 2 2 -5 -3 6 -5 7 2

执行操作 REVERSE 3 6,表示取出数列中从第 3 个数开始的连续 6 个数:

如上所示的灰色部分 2 2 2 -5 -3 6 ,翻转后得到 6 -3 -5 2 2 2 ,并放回原来位置:
2 -6 6 -3 -5 2 2 2 -5 7 2

最后执行 GET-SUM 5 4 和 MAX-SUM ,不难得到答案 1 和 10 。

【评分方法】
本题设有部分分,对于每一个测试点:

  • 如果你的程序能在输出文件正确的位置上打印 GET-SUM 操作的答案,你可以得到该测试点 60% 的分数;
  • 如果你的程序能在输出文件正确的位置上打印 MAX-SUM 操作的答案,你可以得到该测试点 40% 的分数;
  • 以上两条的分数可以叠加,即如果你的程序正确输出所有 GET-SUM 和 MAX-SUM 操作的答案,你可以得到该测试点 100% 的分数。

请注意:如果你的程序只能正确处理某一种操作,请确定在输出文件正确的位置上打印结果,即必须为另一种操作留下对应的行,否则我们不保证可以正确评分。

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

题解:

splay大boss题····能打得来splay操作基本没问题了·····

维护lm,rm,mx,sum,size分别表示包含该节点子树代表的区间的左端点的最大序列和,包含该节点子树代表的区间右端点的最大序列和,totsum:此区间的最大序列和最大值,以及该节点子树的和的总和和该节点子树的大小

然后就是各种提取区间操作了,在此引用Zig_zag题解,%%%%

注:标准结构:区间右端点+1(R)为根,区间左端点-1(L)为根的左儿子,这样目标区间就是L的右儿子,这种形式以后都用"标准结构"代替。

插入操作:先把需要插入的序列建成一个小平衡树(递归),转出标准结构,插到L的右儿子上就行了。

删除操作:转出标准结构,把L的右儿子切下来就行了(注意因为要回收空间,所以还是把要切的子树遍历了一遍,把这颗树上的节点标号入栈)。

覆盖操作:转出标准结构,把L的右儿子打上覆盖标记cov(以后下传的时候把节点的值改为cov的值,sum变为cov*size,la=ra=ma变为cov和sum中较大的一个,因为有负数的情况)

翻转操作:转出标准结构,把L的右儿子打上翻转标记rev(以后下传的时候要交换左右儿子并且交换la和ra)

求和操作:转出标准结构,答案就是L的右儿子的sum

最大值操作:转出标准结构,答案就是L的右儿子的ma

区间操作的时候一定要明白一点,就是打标记的同时做修改,就是说当一个点带了标记的时候,它已经被修改过了。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=1000005;
const int inf=0x3f3f3f3f;
int ans,n,m,father[N],son[N][2],rev[N],sum[N],lm[N],rm[N],mx[N],cov[N],size[N],key[N],a[N],que[N],tail,tot,st,ed,root;
char s[25];
inline int R()
{
  char c;int f=0,i=1;
  for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar());
  if(c==‘-‘)  i=-1,c=getchar();
  for(;c<=‘9‘&&c>=‘0‘;c=getchar())
    f=(f<<3)+(f<<1)+c-‘0‘;
  return f*i;
}
void update(int x)
{
        if (!x) return;
        lm[x]=max(lm[son[x][0]],sum[son[x][0]]+key[x]+max(0,lm[son[x][1]]));
        rm[x]=max(rm[son[x][1]],sum[son[x][1]]+key[x]+max(0,rm[son[x][0]]));
        mx[x]=max(max(mx[son[x][0]],mx[son[x][1]]),key[x]+max(0,rm[son[x][0]])+max(0,lm[son[x][1]]));
        sum[x]=sum[son[x][0]]+sum[son[x][1]]+key[x];
        size[x]=size[son[x][0]]+size[son[x][1]]+1;
}
inline void reverse(int now)
{
  if(!now)  return;
  swap(son[now][1],son[now][0]);
  swap(lm[now],rm[now]);
  rev[now]^=1;
}
inline void cover(int now,int v)
{
  if(!now)  return;
  sum[now]=size[now]*v;key[now]=cov[now]=v;
  lm[now]=rm[now]=mx[now]=max(sum[now],v);
}
void pushdown(int x)
{
        if (!x) return;
        if (rev[x])
        {
                reverse(son[x][0]);
                reverse(son[x][1]);
                rev[x]=0;
        }
        if (cov[x]!=-inf)
        {
                cover(son[x][0],cov[x]);
                cover(son[x][1],cov[x]);
                cov[x]=-inf;
        }
}
inline int get(int now)
{
  return son[father[now]][1]==now;
}
inline void rotate(int now)
{
  int fa=father[now],ofa=father[fa],which=get(now);
  son[fa][which]=son[now][which^1],father[son[fa][which]]=fa;
  son[now][which^1]=fa,father[fa]=now,father[now]=ofa;
  if(ofa)  son[ofa][son[ofa][1]==fa]=now;
  update(fa),update(now);
}
inline void relax(int now,int to)
{
  if(now!=to)  relax(father[now],to);
  pushdown(now);
}
inline void splay(int now,int to)
{
  relax(now,to);
  while(father[now]!=to)
  {
    if(father[father[now]]!=to)  rotate(get(now)==get(father[now])?father[now]:now);
    rotate(now);
  }
  if(!to)  root=now;
}
inline int pick()
{
  if(tail)  return que[tail--];
  else return ++tot;
}
int produce(int x)
{
        int t=pick();
        key[t]=a[x];
        cov[t]=-inf;
        rev[t]=0;
        lm[t]=rm[t]=mx[t]=-inf;
        return t;
}
inline int find(int now,int k)
{
  pushdown(now);
  if(size[son[now][0]]>=k)  return find(son[now][0],k);
  else if(size[son[now][0]]+1==k)  return now;
  else return find(son[now][1],k-size[son[now][0]]-1);
}
int build(int l,int r)
{
        int mid=(l+r)>>1,le=0,ri=0;
        if (l<mid)      le=build(l,mid-1);
        int t=produce(mid);
        if (r>mid)      ri=build(mid+1,r);
        if (le)       son[t][0]=le,father[le]=t;
        if (ri)      son[t][1]=ri,father[ri]=t;
        update(t);
        return t;
}
void del(int &x)
{
        if (!x) return;
        que[++tail]=x;
        father[x]=0;
        del(son[x][0]);
        del(son[x][1]);
        lm[x]=rm[x]=mx[x]=-inf;
        x=0;
}
int main()
{
  //freopen("a.in","r",stdin);
  n=R();m=R();a[st=1]=0;a[ed=n+2]=0;
  for(int i=2;i<=n+1;i++)  a[i]=R();
  rm[0]=lm[0]=mx[0]=-inf;
  root=build(1,n+2);int x,y,z,l,r;
  for (int i=1;i<=m;i++)
        {
                scanf("%s",s);
                if (s[0]==‘I‘)
                {
                        x=R(),y=R();
                        l=find(root,x+1); r=find(root,x+2);
                        splay(r,0); splay(l,root);
                        for (int j=1;j<=y;j++)
                          a[j]=R();
                        int tmp=build(1,y);
                        father[tmp]=l; son[l][1]=tmp;
                        update(l); update(r);
                        splay(tmp,root);
                }
                if (s[0]==‘D‘)
                {
                        x=R(),y=R();
                        r=find(root,x+y+1);splay(r,0);
                        l=find(root,x);splay(l,root);
                        del(son[l][1]);
                        update(l); update(r);
                }
                if (s[0]==‘M‘&&s[2]==‘K‘)
                {
                        x=R(),y=R(),z=R();
                        r=find(root,x+y+1);splay(r,0);
                        l=find(root,x);splay(l,root);
                        cover(son[l][1],z);
                }
                if (s[0]==‘R‘)
                {
                        x=R(),y=R();
                        r=find(root,x+y+1);splay(r,0);
                        l=find(root,x);splay(l,root);
                        reverse(son[l][1]);
                }
                if (s[0]==‘G‘)
                {
                        x=R(),y=R();
                        r=find(root,x+y+1);splay(r,0);
                        l=find(root,x);splay(l,root);
                        ans=sum[son[l][1]];
                        printf("%d\n",ans);
                }
                if (s[0]==‘M‘&&s[2]==‘X‘)
                {
                        splay(ed,0); splay(st,root);
                        ans=mx[son[st][1]];
                        printf("%d\n",ans);
                }
        }
  return 0;
}
时间: 2024-10-11 22:04:24

刷题总结——维护数列(NOI2005 bzoj1500 splay)的相关文章

数据结构(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,

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操作,向输出文件依次打印

【NOI2005】维护数列

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

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

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

7、8月刷题总结

准备开学了囧,7.8月刷题记录,以后好来复习,并且还要好好总结! 数据结构: splay: [BZOJ]1503: [NOI2004]郁闷的出纳员(Splay) [BZOJ]1269: [AHOI2006]文本编辑器editor(Splay) [BZOJ]1507: [NOI2003]Editor(Splay) treap: [BZOJ]1862: [Zjoi2006]GameZ游戏排名系统 & 1056: [HAOI2008]排名系统(treap+非常小心) [BZOJ]3224: Tyvj

bzoj1789 AHOI 维护数列(线段树)

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

Leetcode刷题录之Two Sum

题意大概是给出一个数列num,和一个目标数target,然后要找出数列中的两个数,使得这两个数之和等于目标数,输出这两个数的下标值(从1开始算). 一个比较暴力的方法是用一个二重循环直接遍历序列,在第一重循环中找到a,在第二重循环中找到b,使得a+b=target,这种做法的时间复杂度是O(n^2), 提交时提示超时. 改进方法,先对数列num复制一个副本,然后对副本进行排序.在一重循环中找到a,接着对这个有序的副本进行二分查找,找到b= target-a,二分查找的 时间复杂度是O(logn)

国内有哪些好的刷题网站?

http://www.zhihu.com/question/25574458 Luau Lawrence,Data Mining 弱鸡 / [email protected] 温梦强.石一帆.知乎用户 等人赞同 - Welcome To PKU JudgeOnline 北京大学的Online Judge.POJ上面的题目有点老了,但好处是做的人多,经典算法题多,解题报告也多,适合上手.- ZOJ :: Home 浙江大学的Online Judge.ZOJ用的不多,但为数不多的几次体验好像都还可以

[转]POJ的刷题指南(加了超链接的)

网上看到的转过来一下,顺便把题目都加了个超链接,方便刷起~ POJ上的一些水题(可用来练手和增加自信) (poj3299,poj2159,poj2739,poj1083,poj2262,poj1503,poj3006,poj2255,poj3094) 初期: 一.基本算法:       (1)枚举. (poj1753,poj2965)      (2)贪心(poj1328,poj2109,poj2586)      (3)递归和分治法.       (4)递推.       (5)构造法.(po