NOI2005 维护数列 lg2042
这道题据说是noi题目中算是比较毒瘤的数据结构题了,5k多的代码加上随手写挂细节,我调了两天
题面见https://www.luogu.org/problemnew/show/P2042
(歪个题,这类区间操作的数据结构题可以先去写GSS系列题,会比较容易理解合并的操作)
(再歪个题,我将在近期补一篇博客说一下自己区间树的理解)
一共维护6个操作
第一个操作为插入一棵新的子树,因为是插子树,所以单点插是要t飞的,那么就考虑一个优化,先建好树再挂到他应该出现的位置上
复杂度从nlogn降到了n。
第二个操作删除就把l-1,r+1分别转到根和根的左儿子,那么根的左儿子的右儿子就是题目中需要处理的区间了,直接递归删就完事了
第三个操作翻转就是文艺平衡树里的那样了
第四个区间覆盖的思路也和操作二类似,把需要操作的区间专门转出来,然后打个lazy_tag就好了
第五个操作也同上,把区间转出来之后输出一下sum
第六个操作就直接查询根节点信息里的区间最大值就好了
操作拆开考虑是都不难,但揉在一起的时候就麻了,希望大家能顺利写出这道题。
#include<bits/stdc++.h>
#include<queue>
using namespace std;
#define INF 1000000000
int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int n,m,cnt,root,a[],id[];
queue<int> q;
struct node{
int ch[],sum,cnt,val,f,size,lmax,rmax,maxx;
bool cov,rev;
}st[]; inline bool identify(int p){
return st[st[p].f].ch[]==p;
}//认定自己是父亲的左儿子还是右儿子 inline void connect(int x,int fa,int son){
st[x].f=fa;st[fa].ch[son]=x;return;
}//连接操作 inline void push_up(int x){//上推操作
int ls=st[x].ch[];int rs=st[x].ch[];
st[x].size=st[ls].size+st[rs].size+;//该子树的大小为左右子树大小之和加上一
st[x].sum=st[ls].sum+st[rs].sum+st[x].val;//子树的权值和等于左右子树的和加上当前点的权值
st[x].maxx=max(st[ls].rmax+st[x].val+st[rs].lmax,max(st[ls].maxx,st[rs].maxx));//最大值,左端最大值,右端最大值,记住要加上当前点的权值,其它就是常规操作
st[x].lmax=max(st[ls].lmax,st[ls].sum+st[x].val+st[rs].lmax);
st[x].rmax=max(st[rs].rmax,st[rs].sum+st[x].val+st[ls].rmax);
}
inline void push_down(int x){//下移标记,这个操作我写挂了两次,头麻
int ls=st[x].ch[];int rs=st[x].ch[];
if(st[x].cov){//如果有覆盖操作,翻转就没用了
st[x].cov=st[x].rev=;
if(ls) st[ls].val=st[x].val,st[ls].cov=true,st[ls].sum=st[ls].size*st[ls].val;//有左儿子的话下推标记
if(rs) st[rs].val=st[x].val,st[rs].cov=true,st[rs].sum=st[rs].size*st[rs].val;//有右儿子的话下推标记
if(st[x].val>=){
if(ls) st[ls].lmax=st[ls].rmax=st[ls].maxx=st[ls].sum;
if(rs) st[rs].lmax=st[rs].rmax=st[rs].maxx=st[rs].sum;
}
else{
if(ls) st[ls].lmax=st[ls].rmax=,st[ls].maxx=st[ls].val;
if(rs) st[rs].lmax=st[rs].rmax=,st[rs].maxx=st[rs].val;
}
}
/*
下推标记其实没有什么亮点,就是写起来比较长然后容易写挂细节
这里着重解释一下为什么左右max可以为0,而区间max一定要选择一个值
因为区间合并的时候需要用左右儿子的左右端的最大值,值设为0的意思就是不选择这个区间里的任何东西
如果是写过线段树上维护区间的人可能会想既然不选的东西,值设成负的有什么大不了呢,可这里就有一个
不大一样的操作,左右子树信息上推的时候要加上当前节点的值,如果不把lmax和rmax设成0,就要分多种情况讨论
这代码本来写起来就麻烦了,再分类讨论......所以,当前节点的值若小于0的时候,lmax和rmax就设为0
*/
if(st[x].rev){
st[x].rev^=;st[ls].rev^=;st[rs].rev^=;
swap(st[ls].lmax,st[ls].rmax);swap(st[rs].lmax,st[rs].rmax);
swap(st[ls].ch[],st[ls].ch[]);swap(st[rs].ch[],st[rs].ch[]);//翻转的时候记得换儿子
}
}
inline void rotate(int x){//splay的常规操作就没啥好说的了
int y=st[x].f;int z=st[y].f;
int yson=identify(x);int zson=identify(y);
int b=st[x].ch[yson^];
connect(b,y,yson);connect(y,x,(yson^));connect(x,z,zson);
push_up(y);push_up(x);return;
} inline void splay(int x,int goal){
while(st[x].f!=goal){
int y=st[x].f;int z=st[y].f;
int yson=identify(x);int zson=identify(y);
if(z!=goal){
if(yson==zson) rotate(y);
else rotate(x);
}
rotate(x);
}
if(!goal) root=x;
return;
} inline int find(int p,int rk){//此处运用了一个区间树查询的思想,我会在近期写一份关于区间树的博客,请期待(逃
push_down(p);
int ls=st[p].ch[];int rs=st[p].ch[];
if(st[ls].size+==rk) return p;
if(st[ls].size>=rk) return find(ls,rk);
return find(rs,rk-st[ls].size-);
} inline void recycle(int p){//这辣鸡题卡内存,那么就废物利用呗
if(!p) return;
int ls=st[p].ch[];int rs=st[p].ch[];
recycle(ls);recycle(rs);q.push(p);
st[p].ch[]=st[p].ch[]=st[p].f=;
st[p].rev=st[p].cov=;return;
} inline int split(int k,int tot){//这个操作就找到自己要修改的区间端点的l-1和r+1,分别旋转到根和根的右儿子
int x=find(root,k);int y=find(root,k+tot+);//那么根的右儿子的左儿子就是当前要操作的区间了
splay(x,);splay(y,x);return st[y].ch[];//这个和文艺平衡树的操作类似,可以参考那份代码进行理解
}//最后返回一下当前区间子树的根节点 inline void query(int k,int tot){
int x=split(k,tot);
printf("%d\n",st[x].sum);return;
} inline void cov(int k,int tot,int val){
int x=split(k,tot);int y=st[x].f;
st[x].val=val;st[x].cov=true;st[x].sum=st[x].size*val;
if(val>=){st[x].lmax=st[x].rmax=st[x].maxx=st[x].sum;}
else{st[x].lmax=st[x].rmax=;st[x].maxx=val;}
push_up(y);push_up(st[y].f);return;
}//区间覆盖区间翻转道理都差不多
//我就解释一下区间覆盖把,区间覆盖的时候,当前值改一下,lazy_tag改一下,相应的最大值总和改一下,上推一下,它就很合理......
inline void rev(int k,int tot){
int x=split(k,tot);
int y=st[x].f;int z=st[y].f;
if(!st[x].cov){
st[x].rev^=;swap(st[x].ch[],st[x].ch[]);
swap(st[x].lmax,st[x].rmax);
push_up(y);push_up(st[y].f);
}
} inline void erase(int k,int tot){
int x=split(k,tot);int y=st[x].f;
recycle(x);st[y].ch[]=;
push_up(y);push_up(st[y].f);return;
}//区间删除 inline void build(int l,int r,int f){
if(l>r) return;
int mid=(l+r)>>;int now=id[mid];int last=id[f];
if(l==r){
st[now].sum=a[l];st[now].size=;
st[now].rev=st[now].cov=;
if(a[l]>=){st[now].lmax=st[now].rmax=st[now].maxx=a[l];}
else{st[now].lmax=st[now].rmax=;st[now].maxx=a[l];}
}
else build(l,mid-,mid),build(mid+,r,mid);
st[now].val=a[mid];st[now].f=last;push_up(now);
st[last].ch[mid>=f]=now;return;
}//这个操作它对每个节点进行标号并依照这个进行建树 inline void insert(int k,int 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,);int z=id[(+tot)>>];
int x=find(root,k+);int y=find(root,k+);
splay(x,);splay(y,x);
st[z].f=y;st[y].ch[]=z;
push_up(y);push_up(x);return;
}//在插入一棵新的子树的时候,可以先把这棵子树建出来,然后往原树上一挂,美滋滋
int main(){
n=read();m=read();int i,j,k;//记得给根节点赋极小值
st[].maxx=-INF;memset(a,-0x3f,sizeof(a));
for(i=;i<=n;i++) a[i+]=read();
for(i=;i<=n+;i++) id[i]=i;
build(,n+,);cnt=n+;root=(n+)>>;
int 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);
if(ch[]=='M')
{
if(ch[]=='X')printf("%d\n",st[root].maxx);
else val=read(),cov(k,tot,val);
}
if(ch[]=='R')rev(k,tot);
if(ch[]=='G')query(k,tot);
}
return ;
}
NOI2005 维护数列 lg2042的更多相关文章
- 数据结构(Splay平衡树):COGS 339. [NOI2005] 维护数列
339. [NOI2005] 维护数列 时间限制:3 s 内存限制:256 MB [问题描述] 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际 ...
- [NOI2005] 维护数列
[NOI2005] 维护数列 题目 传送门 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格) 操作编号 输入文件中的格式 说明 1 ...
- P2042 [NOI2005]维护数列 && Splay区间操作(四)
到这里 \(A\) 了这题, \(Splay\) 就能算入好门了吧. 今天是个特殊的日子, \(NOI\) 出成绩, 大佬 \(Cu\) 不敢相信这一切这么快, 一下子机房就只剩我和 \(zrs\) ...
- 洛谷 P2042 [NOI2005]维护数列-Splay(插入 删除 修改 翻转 求和 最大的子序列)
因为要讲座,随便写一下,等讲完有时间好好写一篇splay的博客. 先直接上题目然后贴代码,具体讲解都写代码里了. 参考的博客等的链接都贴代码里了,有空再好好写. P2042 [NOI2005]维护数列 ...
- P2042 [NOI2005]维护数列[splay或非旋treap·毒瘤题]
P2042 [NOI2005]维护数列 数列区间和,最大子列和(必须不为空),支持翻转.修改值.插入删除. 练码力的题,很毒瘤.个人因为太菜了,对splay极其生疏,犯了大量错误,在此记录,望以后一定 ...
- Luogu P2042 [NOI2005]维护数列(平衡树)
P2042 [NOI2005]维护数列 题意 题目描述 请写一个程序,要求维护一个数列,支持以下\(6\)种操作:(请注意,格式栏中的下划线'_'表示实际输入文件中的空格) 输入输出格式 输入格式: ...
- [NOI2005]维护数列(区间splay)
[NOI2005]维护数列(luogu) 打这玩意儿真是要了我的老命 Description 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文 ...
- [转载]无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
转自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182631.html 1500: [NOI2005]维修数列 Time Limit: 10 Sec Mem ...
- [您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MB Description Input 输入的第1 行包含两个数N 和M(M ≤20 ...
随机推荐
- vue中this在回调函数中的使用
this在各类回调中使用: 如果是普通函数是没法使用的 所以需要先将this.变量赋值给新的变量,然后才能在回调函数中使用 例如: refund: function (id) { if (!this. ...
- 阿里巴巴Java开发手册之并发处理注意事项
1. [强制]获取单例对象需要保证线程安全,其中的方法也要保证线程安全.说明:资源驱动类.工具类.单例工厂类都需要注意.2. [强制]创建线程或线程池时请指定有意义的线程名称,方便出错时回溯.正例:p ...
- [SDOI2012] 任务安排 题解
有感而发,遂书. 其实和sze聊了很久,但他还是退役了.恐怕他是本届里学oi时间最长的一个人吧,从小学五年级开始.我也是因为他,才开始学oi的.他因为学校的压力,不得不放弃.或许是没什么天赋.学了4年 ...
- pads无模命令
W<n>………改变线宽,比如 30. 栅格(Grids) G<xx>………过孔和设计栅格设置.GD<xx>………显示栅格设置.GP………打开或关闭极性栅格.GP r ...
- JAVA输出流与输入流
输出流 编程入门的第一个程序,输出一串字符串 public class C { public static void main(String[] args) { System.out.println( ...
- 清北学堂—2020.1提高储备营—Day 2 morning(并查集、堆)
qbxt Day 2 morning --2020.1.18 济南 主讲:李佳实 目录一览 1.并查集 2.堆 总知识点:基础数据结构 一.并查集 1.描述:并查集是一类十分常用的数据类型,它有着十分 ...
- 【新人赛】阿里云恶意程序检测 -- 实践记录 11.24 - word2vec模型 + xgboost
使用word2vec训练词向量 使用word2vec无监督学习训练词向量,输入的是训练数据和测试数据,输出的是每个词的词向量,总共三百个词左右. 求和:然后再将每行数据中的每个词的词向量加和,得到每行 ...
- Java【第二课 扫描仪 & 布尔数据类型】
一.Java扫描仪 为了更加方便的理解,我先将逻辑框图 这个有点像C语言的scan()的用法 import java.util.Scanner; //导入扫描仪 public class demo{ ...
- 对象浅拷贝Object.assign
const target = { a: { b: { c: { d: 1 } }, e: 5, f: 6, h: 10 } } const source = { a: { b: { c: { d: 1 ...
- 复习promise---node
promise这个东西,我都不知道见过多少回了!,非常重要,这里在回忆一遍 发现问题 const fs = require('fs') fs.readFile('./data/1.txt', (err ...