[Submit][Status][Discuss]

Description

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

Input

输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目。
第2行包含N个数字,描述初始时的数列。
以下M行,每行一条命令,格式参见问题描述中的表格。
任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内。
插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。

Output

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

Sample Input

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

Sample Output

-1
10
1
10

HINT

题解:Splay tree模板题;
自己的板子:
 #include<bits/stdc++.h>
#define RI register int
#define For(i,a,b) for (RI i=a;i<=b;++i)
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e6+;
int n,m,rt,cnt;
int a[N],id[N],fa[N],c[N][];
int sum[N],sz[N],v[N],mx[N],lx[N],rx[N];
bool tag[N],rev[N];
//tag表示是否有统一修改的标记,rev表示是否有统一翻转的标记
//sum表示这个点的子树中的权值和,v表示这个点的权值
//lx[]是一个子树以最左端为起点向右延伸的最大子串和,rx类似
//mx[]是当前子树的最大子串和
queue<int> q;
inline int read()
{
RI x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-') f=-; ch=getchar();}
while(''<=ch&&ch<=''){x=(x<<)+(x<<)+ch-'';ch=getchar();}
return x*f;
}
inline void pushup(RI x)
{
RI l=c[x][],r=c[x][];
sum[x]=sum[l]+sum[r]+v[x];
sz[x]=sz[l]+sz[r]+;
mx[x]=max(mx[l],max(mx[r],rx[l]+v[x]+lx[r]));
lx[x]=max(lx[l],sum[l]+v[x]+lx[r]);
rx[x]=max(rx[r],sum[r]+v[x]+rx[l]);
}
//上传记录标记
inline void pushdown(RI x)
{
RI l=c[x][],r=c[x][];
if(tag[x])
{
rev[x]=tag[x]=;//我们有了一个统一修改的标记,再翻转就没有什么意义了
if(l) tag[l]=,v[l]=v[x],sum[l]=v[x]*sz[l];
if(r) tag[r]=,v[r]=v[x],sum[r]=v[x]*sz[r];
if(v[x]>=)
{
if(l) lx[l]=rx[l]=mx[l]=sum[l];
if(r) lx[r]=rx[r]=mx[r]=sum[r];
}
else
{
if(l) lx[l]=rx[l]=,mx[l]=v[x];
if(r) lx[r]=rx[r]=,mx[r]=v[x];
}
}
if(rev[x])
{
rev[x]=;rev[l]^=;rev[r]^=;
swap(lx[l],rx[l]);swap(lx[r],rx[r]);
//注意,在翻转操作中,前后缀的最大和子序列都反过来了
swap(c[l][],c[l][]);swap(c[r][],c[r][]);
}
}
inline void rotate(RI x,RI &k)
{
RI y=fa[x],z=fa[y],l=(c[y][]==x),r=l^;
if (y==k)k=x;else c[z][c[z][]==y]=x;
fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
//旋转操作,一定要上传标记且顺序不能变
}
inline void splay(RI x,RI &k)
{
while(x!=k)
{
int y=fa[x],z=fa[y];
if(y!=k)
{
if((c[z][]==y)^(c[y][]==x)) rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
}
//这是整个程序的核心之一,毕竟是伸展操作嘛
inline int find(RI x,RI rk)
{//返回当前序列第rk个数的标号
pushdown(x);
RI l=c[x][],r=c[x][];
if(sz[l]+==rk) return x;
if(sz[l]>=rk) return find(l,rk);
else return find(r,rk-sz[l]-);
}
inline void recycle(RI x)
{//这就是用时间换空间的回收冗余编号机制,很好理解
RI &l=c[x][],&r=c[x][];
if(l) recycle(l);
if(r) recycle(r);
q.push(x);
fa[x]=l=r=tag[x]=rev[x]=;
}
inline int split(RI k,RI tot)//找到[k+1,k+tot]
{
RI x=find(rt,k),y=find(rt,k+tot+);
splay(x,rt);splay(y,c[x][]);
return c[y][];
}
//这个split操作是整个程序的核心之三
//我们通过这个split操作,找到[k+1,k+tot],并把k,和k+tot+1移到根和右儿子的位置
//然后我们返回了这个右儿子的左儿子,这就是我们需要操作的区间
inline void query(RI k,RI tot)
{
RI x=split(k,tot);
printf("%d\n",sum[x]);
}
inline void modify(RI k,RI tot,RI val)//MAKE-SAME
{
RI x=split(k,tot),y=fa[x];
v[x]=val;tag[x]=;sum[x]=sz[x]*val;
if(val>=) lx[x]=rx[x]=mx[x]=sum[x];
else lx[x]=rx[x]=,mx[x]=val;
pushup(y);pushup(fa[y]);
//每一步的修改操作,由于父子关系发生改变
//及记录标记发生改变,我们需要及时上传记录标记
}
inline void rever(RI k,RI tot)//翻转
{
RI x=split(k,tot),y=fa[x];
if(!tag[x])
{
rev[x]^=;
swap(c[x][],c[x][]);
swap(lx[x],rx[x]);
pushup(y);pushup(fa[y]);
}
//同上
}
inline void erase(RI k,RI tot)//DELETE
{
RI x=split(k,tot),y=fa[x];
recycle(x);c[y][]=;
pushup(y);pushup(fa[y]);
//同上
}
inline void build(RI l,RI r,RI f)
{
RI mid=(l+r)>>,now=id[mid],pre=id[f];
if(l==r)
{
mx[now]=sum[now]=a[l];
tag[now]=rev[now]=;
//这里这个tag和rev的清0是必要,因为这个编号可能是之前冗余了
lx[now]=rx[now]=max(a[l],);
sz[now]=;
}
if(l<mid) build(l,mid-,mid);
if(mid<r) build(mid+,r,mid);
v[now]=a[mid]; fa[now]=pre;
pushup(now); //上传记录标记
c[pre][mid>=f]=now;
//当mid>=f时,now是插入到又区间取了,所以c[pre][1]=now,当mid<f时同理
}
inline void insert(RI k,RI tot)
{
for(int i=;i<=tot;++i) a[i]=read();
for(int i=;i<=tot;++i)
{
if(!q.empty()) id[i]=q.front(),q.pop();
else id[i]=++cnt;//利用队列中记录的冗余节点编号
}
build(,tot,);
RI z=id[(+tot)>>];
RI x=find(rt,k+),y=find(rt,k+);
//首先,依据中序遍历,找到我们需要操作的区间的实际编号
splay(x,rt);splay(y,c[x][]);
//把k+1(注意我们已经右移了一个单位)和(k+1)+1移到根和右儿子
fa[z]=y;c[y][]=z;
//直接把需要插入的这个平衡树挂到右儿子的左儿子上去就好了
pushup(y);pushup(x);
//上传记录标记
}
//可以这么记,只要用了split就要重新上传标记
//只有find中需要下传标记
int main()
{
n=read(),m=read();
mx[]=a[]=a[n+]=-inf;
For(i,,n) a[i+]=read();
For(i,,n+) id[i]=i;//虚拟了两个节点1和n+2,然后把需要操作区间整体右移一个单位
build(,n+,);//建树
rt=(n+)>>;cnt=n+;//取最中间的为根
RI k,tot,val;char ch[];
while(m--)
{
scanf("%s",ch);
if(ch[]!='M' || ch[]!='X') k=read(),tot=read();
if(ch[]=='I') insert(k,tot);
if(ch[]=='D') erase(k,tot);//DELETE
if(ch[]=='M')
{
if(ch[]=='X') printf("%d\n",mx[rt]);//MAX-SUM
else val=read(),modify(k,tot,val);//MAKE-SAME
}
if(ch[]=='R') rever(k,tot);//翻转
if(ch[]=='G') query(k,tot);//GET-SUM
}
return ;
}

斌神的板子

 /**************************************************************
Problem: 1500
User: SongHL
Language: C++
Result: Accepted
Time:6152 ms
Memory:26684 kb
****************************************************************/ #include<bits/stdc++.h>
using namespace std; #define Key_value ch[ch[root][1]][0]
const int MAXN = ;
const int INF = 0x3f3f3f3f;
int pre[MAXN],ch[MAXN][],key[MAXN],size[MAXN];
int root,tot1;
int sum[MAXN],rev[MAXN],same[MAXN];
int lx[MAXN],rx[MAXN],mx[MAXN];
int s[MAXN],tot2;//内存池和容量
int a[MAXN];
int n,q; void NewNode(int &r,int father,int k)
{
if(tot2) r = s[tot2--];//取的时候是tot2--,存的时候就是++tot2
else r = ++tot1;
pre[r] = father;
ch[r][] = ch[r][] = ;
key[r] = k;
sum[r] = k;
rev[r] = same[r] = ;
lx[r] = rx[r] = mx[r] = k;
size[r] = ;
}
void Update_Rev(int r)
{
if(!r)return;
swap(ch[r][],ch[r][]);
swap(lx[r],rx[r]);
rev[r] ^= ;
}
void Update_Same(int r,int v)
{
if(!r)return;
key[r] = v;
sum[r] = v*size[r];
lx[r] = rx[r] = mx[r] = max(v,v*size[r]);
same[r] = ;
}
void push_up(int r)
{
int lson = ch[r][], rson = ch[r][];
size[r] = size[lson] + size[rson] + ;
sum[r] = sum[lson] + sum[rson] + key[r];
lx[r] = max(lx[lson],sum[lson] + key[r] + max(,lx[rson]));
rx[r] = max(rx[rson],sum[rson] + key[r] + max(,rx[lson]));
mx[r] = max(,rx[lson]) + key[r] + max(,lx[rson]);
mx[r] = max(mx[r],max(mx[lson],mx[rson]));
}
void push_down(int r)
{
if(same[r])
{
Update_Same(ch[r][],key[r]);
Update_Same(ch[r][],key[r]);
same[r] = ;
}
if(rev[r])
{
Update_Rev(ch[r][]);
Update_Rev(ch[r][]);
rev[r] = ;
}
}
void Build(int &x,int l,int r,int father)
{
if(l > r)return;
int mid = (l+r)/;
NewNode(x,father,a[mid]);
Build(ch[x][],l,mid-,x);
Build(ch[x][],mid+,r,x);
push_up(x);
}
void Init()
{
root = tot1 = tot2 = ;
ch[root][] = ch[root][] = size[root] = pre[root] = ;
same[root] = rev[root] = sum[root] = key[root] = ;
lx[root] = rx[root] = mx[root] = -INF;
NewNode(root,,-);
NewNode(ch[root][],root,-);
for(int i = ;i < n;i++)
scanf("%d",&a[i]);
Build(Key_value,,n-,ch[root][]);
push_up(ch[root][]);
push_up(root);
}
//旋转,0为左旋,1为右旋
void Rotate(int x,int kind)
{
int y = pre[x];
push_down(y);
push_down(x);
ch[y][!kind] = ch[x][kind];
pre[ch[x][kind]] = y;
if(pre[y])
ch[pre[y]][ch[pre[y]][]==y] = x;
pre[x] = pre[y];
ch[x][kind] = y;
pre[y] = x;
push_up(y);
}
//Splay调整,将r结点调整到goal下面
void Splay(int r,int goal)
{
push_down(r);
while(pre[r] != goal)
{
if(pre[pre[r]] == goal)
{
push_down(pre[r]);
push_down(r);
Rotate(r,ch[pre[r]][] == r);
}
else
{
push_down(pre[pre[r]]);
push_down(pre[r]);
push_down(r);
int y = pre[r];
int kind = ch[pre[y]][]==y;
if(ch[y][kind] == r)
{
Rotate(r,!kind);
Rotate(r,kind);
}
else
{
Rotate(y,kind);
Rotate(r,kind);
}
}
}
push_up(r);
if(goal == ) root = r;
}
int Get_kth(int r,int k)
{
push_down(r);
int t = size[ch[r][]] + ;
if(t == k)return r;
if(t > k)return Get_kth(ch[r][],k);
else return Get_kth(ch[r][],k-t);
} //在第pos个数后面插入tot个数
void Insert(int pos,int tot)
{
for(int i = ;i < tot;i++)scanf("%d",&a[i]);
Splay(Get_kth(root,pos+),);
Splay(Get_kth(root,pos+),root);
Build(Key_value,,tot-,ch[root][]);
push_up(ch[root][]);
push_up(root);
} //删除子树
void erase(int r)
{
if(!r)return;
s[++tot2] = r;
erase(ch[r][]);
erase(ch[r][]);
}
//从第pos个数开始连续删除tot个数
void Delete(int pos,int tot)
{
Splay(Get_kth(root,pos),);
Splay(Get_kth(root,pos+tot+),root);
erase(Key_value);
pre[Key_value] = ;
Key_value = ;
push_up(ch[root][]);
push_up(root);
}
//将从第pos个数开始的连续的tot个数修改为c
void Make_Same(int pos,int tot,int c)
{
Splay(Get_kth(root,pos),);
Splay(Get_kth(root,pos+tot+),root);
Update_Same(Key_value,c);
push_up(ch[root][]);
push_up(root);
} //将第pos个数开始的连续tot个数进行反转
void Reverse(int pos,int tot)
{
Splay(Get_kth(root,pos),);
Splay(Get_kth(root,pos+tot+),root);
Update_Rev(Key_value);
push_up(ch[root][]);
push_up(root);
}
//得到第pos个数开始的tot个数的和
int Get_Sum(int pos,int tot)
{
Splay(Get_kth(root,pos),);
Splay(Get_kth(root,pos+tot+),root);
return sum[Key_value];
}
//得到第pos个数开始的tot个数中最大的子段和
int Get_MaxSum(int pos,int tot)
{
Splay(Get_kth(root,pos),);
Splay(Get_kth(root,pos+tot+),root);
return mx[Key_value];
} void InOrder(int r)
{
if(!r)return;
push_down(r);
InOrder(ch[r][]);
printf("%d ",key[r]);
InOrder(ch[r][]);
} int main()
{
while(scanf("%d%d",&n,&q) == )
{
Init();
char op[];
int x,y,z;
while(q--)
{
scanf("%s",op);
if(strcmp(op,"INSERT") == )
{
scanf("%d%d",&x,&y);
Insert(x,y);
}
else if(strcmp(op,"DELETE") == )
{
scanf("%d%d",&x,&y);
Delete(x,y);
}
else if(strcmp(op,"MAKE-SAME") == )
{
scanf("%d%d%d",&x,&y,&z);
Make_Same(x,y,z);
}
else if(strcmp(op,"REVERSE") == )
{
scanf("%d%d",&x,&y);
Reverse(x,y);
}
else if(strcmp(op,"GET-SUM") == )
{
scanf("%d%d",&x,&y);
printf("%d\n",Get_Sum(x,y));
}
else if(strcmp(op,"MAX-SUM") == )
printf("%d\n",Get_MaxSum(,size[root]-));
}
}
return ;
}

  

 

BZOJ1500 [NOI2005]维修数列(Splay tree)的更多相关文章

  1. BZOJ 1500: [NOI2005]维修数列 (splay tree)

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 4229  Solved: 1283[Submit][Status ...

  2. BZOJ1500: [NOI2005]维修数列[splay ***]

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 12278  Solved: 3880[Submit][Statu ...

  3. [NOI2005]维修数列 Splay tree 区间反转,修改,求和,求最值

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1500 Description Input 输入文件的第1行包含两个数N和M,N表示初始时数 ...

  4. [bzoj1500][NOI2005]维修数列——splay

    题目 题解 这道题可以说是数列问题的大BOSS,也算是这一周来学习splay等数据结构的一个总结. 我们一个一个地看这些操作. 对于操作1,我们首先建一棵子树,直接接上原树即可. 对于操作2,我们找到 ...

  5. BZOJ1500: [NOI2005]维修数列 [splay序列操作]【学习笔记】

    以前写过这道题了,但我把以前的内容删掉了,因为现在感觉没法看 重写! 题意: 维护一个数列,支持插入一段数,删除一段数,修改一段数,翻转一段数,查询区间和,区间最大子序列 splay序列操作裸题 需要 ...

  6. [bzoj1500][NOI2005 维修数列] (splay区间操作)

    Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目. 第2行包含N个数字,描述初始时的数列. 以下M行,每 ...

  7. bzoj1500: [NOI2005]维修数列 (Splay+变态题)

    Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 11353  Solved: 3553 [Submit][Status][Discuss] Descrip ...

  8. [BZOJ1500][NOI2005]维修数列 解题报告 Splay

    Portal Gun:[BZOJ1500][NOI2005]维修数列 有一段时间没写博客了,最近在刚数据结构......各种板子背得简直要起飞,题目也是一大堆做不完,这里就挑一道平衡树的题来写写好了 ...

  9. [BZOJ1500][NOI2005]维修数列---解题报告

    Portal Gun:[BZOJ1500][NOI2005]维修数列 有一段时间没写博客了,最近在刚数据结构......各种板子背得简直要起飞,题目也是一大堆做不完,这里就挑一道平衡树的题来写写好了 ...

随机推荐

  1. js正则匹配的出链接地址

    content为需要匹配的值 var b=/<a([\s]+|[\s]+[^<>]+[\s]+)href=(\"([^<>"\']*)\"| ...

  2. PHP 在Swoole中使用双IoC容器实现无污染的依赖注入

    简介: 容器(container)技术(可以理解为全局的工厂方法), 已经是现代项目的标配. 基于容器, 可以进一步实现控制反转, 依赖注入. Laravel 的巨大成功就是构建在它非常强大的IoC容 ...

  3. pat 1006 Sign In and Sign Out(25 分)

    1006 Sign In and Sign Out(25 分) At the beginning of every day, the first person who signs in the com ...

  4. 【SpringBoot | Swagger】SpringBoot整合Swagger

    SpringBoot整合Swagger 1. 什么是Swagger Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.简单说就是项目跑起来了, ...

  5. 拎壶学python3-----(5)python之格式化输出

    一.格式化输入可以减少代码开发量如下是格式化输出: 上边的%是什么意思呢?%是一个占位符,s代表字符串的类型. 二.我们看下边的例子 看使用%d %i的用法. %d %i 这种格式化只能用数字来填补占 ...

  6. code migrate

    1. 从Git上clone 仓库到本地. git clone --mirror http://gittest:gittest@192.168.1.x/x.git 2. push 到codecommit ...

  7. PostGIS 结合Openlayers以及Geoserver实现最短路径分析(三)

    接上篇,前面在ArcMap中和Postgis中将数据都已经进行了预处理. 接下来回到Geoserver中,进行数据发布. 1.新建工作区 2.填写完工作区信息 3.打开数据存储,添加新的数据存储 4. ...

  8. Netflix 开源 Polynote:对标 Jupyter,一个笔记本运行多种语言

    谈到数据科学领域的开发工具,Jupyter 无疑是非常知名的一种.它具有灵活高效的特点,非常适合进行开发.调试.分享和教学.近日,Netflix(奈飞)居然也玩起了跨界,他们开源了一个名为 Polyn ...

  9. 20191010-6 alpha week 1/2 Scrum立会报告+燃尽图 04

    此作业的要求参见[https://edu.cnblogs.com/campus/nenu/2019fall/homework/8749] 一.小组情况 队名:扛把子 组长:迟俊文 组员:宋晓丽 梁梦瑶 ...

  10. 2019-11-7:练习上传getshell,通过菜刀连接

    一.使用phtstudy搭建环境 1,将emlog源码解压到phpstudy的www目录,复制emlog源码中的src所有文件到需要搭建emlog的目录中,修改emlog配置文件,输入好本机mysql ...