洛谷.2042.[NOI2005]维护数列(Splay)
2017.12.24 第一次写:
时间: 2316ms (1268ms)
空间: 19.42MB (19.5MB)(O2)
注:洛谷测的时间浮动比较大
/*
插入一段数:将这些数先单独建一棵平衡树t'.将pos旋到根,pos+1旋到son[root][1],直接将t'放到son[pos+1][0]即可
删除一段数:将要删除区间旋转到r+1的左儿子上,直接删除
统一修改一段数、区间反转:同样,提取修改区间,打标记
区间求和:提取,输出记录的sum
最大子序列:有一种求最大子序列的nlogn分治算法,用于带修改的最大子序列
就是记录lmax,rmax,maxn,sum(是否包含区间左/右端点),dp思想更新
1.最多会出现4*10^6个数,任意时刻只会有5*10^5个数,所以要用一个栈或队列存储可用下标(内存池)
要id[]记录
有Delete掉的下标就存;栈里没有就++cnt
Build时一定要分清是位置m还是下标rt
2.sum[ls],sum[rs]都是不包括val[rt]的,转移时注意加上
3.下传rt节点时是去用标记更新ls,rs,而不是还更新rt。没理解透。
更新rt的标记时要更新rt各值!包括rev。在下传时给子节点标记同时更新子节点
也许不这样会导致标记下传不完全,因为这题是多询问的,而文艺平衡树是在最后输出,输出时一定会更新标记。
4.区间反转会导致lmx,rmx反了,要换!
5.Down()一般只需要在Find向下递归时进行就可以
6.由于每次Split都会导致父子关系改变,所以每次更新都要Update(son[root]),Update(root)
7.初始化mx[0],这样更新最值可以不用管是否存在子节点
8.mx和lmx,rmx更新方式是不一样的!
注意转移
*/
#include<cstdio>
#include<cctype>
#include<algorithm>
#define gc() getchar()
const int N=5e5+5,INF=1e9;
int n,root,A[N],id[N],sk[N],tp,cnt;
int fa[N],son[N][2],sz[N],val[N],lmx[N],rmx[N],mx[N],sum[N];
bool tag[N],rev[N];//直接用val[rt]的值代替int tag[rt]
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now*f;
}
#define ls son[rt][0]
#define rs son[rt][1]
inline void Update(int rt)
{
sz[rt]=sz[ls]+sz[rs]+1;
mx[rt]=std::max(mx[ls],std::max(mx[rs],rmx[ls]+val[rt]+lmx[rs]));
lmx[rt]=std::max(lmx[ls],sum[ls]+val[rt]+lmx[rs]);
rmx[rt]=std::max(rmx[rs],sum[rs]+val[rt]+rmx[ls]);
sum[rt]=sum[ls]+val[rt]+sum[rs];
}
inline void Down(int rt)
{
if(tag[rt])
{
if(ls)
{
tag[ls]=1, val[ls]=val[rt], sum[ls]=sz[ls]*val[rt];
if(val[rt]>=0) lmx[ls]=rmx[ls]=mx[ls]=sum[ls];
else lmx[ls]=rmx[ls]=0,mx[ls]=val[rt];
}
if(rs)
{
tag[rs]=1, val[rs]=val[rt], sum[rs]=sz[rs]*val[rt];
if(val[rt]>=0) lmx[rs]=rmx[rs]=mx[rs]=sum[rs];
else lmx[rs]=rmx[rs]=0,mx[rs]=val[rt];
}
rev[rt]=tag[rt]=0;//有修改标记就不需要反转
}
else if(rev[rt])
{
rev[rt]=0, rev[ls]^=1, rev[rs]^=1;
std::swap(lmx[ls],rmx[ls]), std::swap(son[ls][0],son[ls][1]);
std::swap(lmx[rs],rmx[rs]), std::swap(son[rs][0],son[rs][1]);
// WA: std::swap(lmx[rt],rmx[rt]);
// std::swap(ls,rs);
}
}
void Rotate(int x,int &k)
{
int a=fa[x],b=fa[a];bool l=son[a][1]==x,r=l^1;
if(a==k) k=x;
else son[b][son[b][1]==a]=x;
fa[x]=b, fa[a]=x, fa[son[x][r]]=a;
son[a][l]=son[x][r], son[x][r]=a;
Update(a),Update(x);
}
void Splay(int x,int &k)
{
while(x!=k)
{
int a=fa[x],b=fa[a];
if(a!=k) (son[a][1]==x^son[b][1]==a)?Rotate(x,k):Rotate(a,k);
Rotate(x,k);
}
}
int Rank(int rt,int k)
{
while(1)
{
Down(rt);
if(sz[ls]+1==k) return rt;
if(sz[ls]>=k) rt=ls;
else k-=sz[ls]+1,rt=rs;
}
}
void Build(int l,int r,int f)
{
if(l>r) return;
int m=l+r>>1,rt=id[m],pre=id[f];
fa[rt]=pre, son[pre][m>f]=rt;/*,sz[rt]=1;*///son[pre][m>f]:注意数组下标和位置没有关系
rev[rt]=tag[rt]=0, val[rt]=A[m];//因为sum,lmx等还会在Update重新更新
if(l==r)//所以只在这时更新这些值
{
mx[rt]=sum[rt]=A[l], sz[rt]=1;//mx[rt]是这个区间(点)必须要选,所以就是A[l]
lmx[rt]=rmx[rt]=std::max(val[rt],0);
return;
}
Build(l,m-1,m), Build(m+1,r,m);
Update(rt);
}
void Free(int rt)//时间换空间,回收无用编号
{
if(!rt) return;
sk[++tp]=rt;
Free(ls), Free(rs);
fa[rt]=ls=rs=0;
// rev[rt]=tag[rt]=0;
}
int Split(int pos,int tot)
{
int x=Rank(root,pos),y=Rank(root,pos+tot+1);
Splay(x,root), Splay(y,son[x][1]);
return son[y][0];
}
void Ins()
{
int pos=read()+1,tot=read();
for(int i=1;i<=tot;++i) A[i]=read();
for(int i=1;i<=tot;++i)
if(tp) id[i]=sk[tp--];
else id[i]=++cnt;
Build(1,tot,0);
int z=id[1+tot>>1],x=Rank(root,pos),y=Rank(root,pos+1);//z:the root of t'
Splay(x,root), Splay(y,son[x][1]);
fa[z]=y, son[y][0]=z;
Update(y), Update(x);
}
void Del()
{
int pos=read(),tot=read(),y=Split(pos,tot),x=fa[y];
Free(y), son[x][0]=0;
Update(x), Update(fa[x]);//不要再用y!
}
void Modify()
{
int pos=read(),tot=read(),v=read(),rt=Split(pos,tot);
tag[rt]=1,val[rt]=v,sum[rt]=sz[rt]*v;
if(v>=0) lmx[rt]=rmx[rt]=sum[rt], mx[rt]=sum[rt];//lmx,rmx全选或不选
else lmx[rt]=rmx[rt]=0, mx[rt]=v;//mx全选或选一个
Update(fa[rt]), Update(root);
}
void Rev()
{
int pos=read(),tot=read(),rt=Split(pos,tot);
if(!tag[rt])
{
rev[rt]^=1;
std::swap(ls,rs), std::swap(lmx[rt],rmx[rt]);
Update(fa[rt]), Update(root);
}
//WA:rev[rt]^=1;
// Update(fa[rt]), Update(root);
}
void Query()
{
int pos=read(),tot=read(),rt=Split(pos,tot);
printf("%d\n",sum[rt]);
}
//void Print(int x)
//{
// if(!x) return;
// Down(x);
// Print(son[x][0]), printf("%d ",val[x]), Print(son[x][1]);
//}
int main()
{
//#ifndef ONLINE_JUDGE
// freopen("2042.in","r",stdin);
//#endif
// freopen("seq2005.in","r",stdin);
// freopen("seq2005.out","w",stdout);
n=read();int q=read();
mx[0]=A[1]=A[n+2]=-INF;
for(int i=1;i<=n;++i) A[i+1]=read(),id[i]=i;
id[n+1]=n+1,id[n+2]=n+2;
Build(1,n+2,0), root=n+3>>1, cnt=n+2;
char s[12];
while(q--)
{
scanf("%s",s);
if(s[0]=='I') Ins();
else if(s[0]=='D') Del();
else if(s[2]=='K') Modify();
else if(s[0]=='R') Rev();
else if(s[0]=='G') Query();
else printf("%d\n",mx[root]);
}
return 0;
}
2018.2.26 第二次写:(简化许多)(两份实际行数倒差不多)
时间: 2008ms (1536ms)
空间:19.51MB (19.49MB)
/*
数列位置直接拿sz解决
需要存个id,下标并不统一
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
const int N=5e5+15,INF=1e8,MAXIN=1e6;
int n,m,root;
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now*f;
}
struct SPLAY
{
int A[N],size,fa[N],son[N][2],val[N],sz[N],id[N],sum[N],lmax[N],rmax[N],smax[N];
int pool[N],top,cnt;
bool cover[N],rev[N];
inline int New_Node(){
if(top) return pool[--top];
return ++cnt;
}
inline void Update(int rt)
{
int ls=son[rt][0],rs=son[rt][1];
sz[rt]=sz[ls]+sz[rs]+1;
sum[rt]=sum[ls]+val[rt]+sum[rs];
smax[rt]=std::max(rmax[ls]+val[rt]+lmax[rs],std::max(smax[ls],smax[rs]));
lmax[rt]=std::max(lmax[ls],sum[ls]+val[rt]+lmax[rs]);
rmax[rt]=std::max(rmax[rs],sum[rs]+val[rt]+rmax[ls]);
}
inline void Set(int rt,int v)
{
sum[rt]=v*sz[rt], val[rt]=v, cover[rt]=1;
if(v>0) smax[rt]=lmax[rt]=rmax[rt]=sum[rt];
else smax[rt]=v, lmax[rt]=rmax[rt]=0;//至少存在一元素 但是作为区间端点还是可以不要的
}
inline void Rev(int rt)
{
std::swap(lmax[rt],rmax[rt]), std::swap(son[rt][0],son[rt][1]);
rev[rt]^=1;
}
void PushDown(int rt)
{
if(cover[rt])
Set(son[rt][0],val[rt]), Set(son[rt][1],val[rt]), cover[rt]=rev[rt]=0;//这个rev没有必要了
if(rev[rt])
Rev(son[rt][0]), Rev(son[rt][1]), rev[rt]^=1;
}
void Rotate(int x,int &k)
{
int a=fa[x],b=fa[a]; bool l=son[a][1]==x,r=l^1;
if(k==a) k=x;
else son[b][son[b][1]==a]=x;
fa[a]=x, fa[x]=b, fa[son[x][r]]=a,
son[a][l]=son[x][r], son[x][r]=a;
Update(a), Update(x);
}
void Splay(int x,int &k)
{
while(x!=k)
{
int a=fa[x],b=fa[a];
// PushDown(b), PushDown(a), PushDown(x);
if(a!=k) son[a][1]==x^son[b][1]==a?Rotate(x,k):Rotate(a,k);
Rotate(x,k);
}
}
int Rank(int k,int p)
{
while(1)
{
PushDown(k);
if(sz[son[k][0]]+1==p) return k;
if(sz[son[k][0]]>=p) k=son[k][0];
else p-=sz[son[k][0]]+1, k=son[k][1];
}
}
void Build(int l,int r,int f)
{
if(l>r) return;
int m=l+r>>1,rt=id[m],pre=id[f];
val[rt]=A[m], fa[rt]=pre, son[pre][m>f]=rt;//son[pre][val[rt]>val[pre]]=rt;同样的错误--在树中的排名是下标!
cover[rt]=rev[rt]=0;
if(l==r){
sz[rt]=1, sum[rt]=smax[rt]=val[rt];
if(val[rt]>0) lmax[rt]=rmax[rt]=val[rt];
else lmax[rt]=rmax[rt]=0;
}
else{//其余信息在Update时更新
Build(l,m-1,m), Build(m+1,r,m),
Update(rt);
}
}
void Insert()
{
int p=read()+1,tot=read();
for(int i=1; i<=tot; ++i) A[i]=read(),id[i]=New_Node();
Build(1,tot,0);
int r=id[1+tot>>1],x=Rank(root,p),y=Rank(root,p+1);
Splay(x,root), Splay(y,son[x][1]);
fa[r]=y, son[y][0]=r;
Update(y), Update(x);
}
void Free(int x)
{
pool[top++]=x;
if(son[x][0]) Free(son[x][0]);
if(son[x][1]) Free(son[x][1]);
son[x][0]=son[x][1]=0;//son不会在Build时更新
// fa[x]=rev[x]=cover[x]=0;
}
int Split()
{
int p=read(),tot=read();
int x=Rank(root,p),y=Rank(root,p+tot+1);
Splay(x,root), Splay(y,son[x][1]);
// printf("p:%d tot:%d x:%d y:%d z:%d\n",p,tot,x,y,son[y][0]);
return son[y][0];
}
void Delete()
{
int z=Split(),y=fa[z];
/*if(z)*/ Free(z);//当前z这里不需要特判 不会删空元素
son[y][0]=0, Update(y), Update(fa[y]);
}
void Modify(){
int z=Split(),v=read();
Set(z,v), Update(fa[z]), Update(root);//更新!
}
void Reverse(){
int rt=Split();
if(!cover[rt])//在无cover标记时反转
Rev(rt), Update(fa[rt]), Update(root);
}
int Query_Sum(){
printf("%d\n",sum[Split()]);
}
// void Print(int x)
// {
// PushDown(x);
// if(son[x][0]) Print(son[x][0]);
// printf("%d:%d\n",x,val[x]);
// if(son[x][1]) Print(son[x][1]);
// }
// void Debug()
// {
// puts("Debug");
// Print(root);
// putchar('\n');
// }
}t;
int main()
{
n=read(),m=read();
t.id[1]=1, t.id[n+2]=n+2;//不能用0
t.A[1]=t.A[n+2]=-INF;//边界两个点要处理不能算答案!
for(int i=2; i<=n+1; ++i) t.A[i]=read(),t.id[i]=i;
t.cnt=n+2, t.Build(1,n+2,0);
root=t.id[n+3>>1];
char opt[15];
while(m--)
{
scanf("%s",opt);
if(opt[0]=='I') t.Insert();
else if(opt[0]=='D') t.Delete();
else if(opt[4]=='-') t.Modify();
else if(opt[0]=='R') t.Reverse();
else if(opt[0]=='G') t.Query_Sum();
else printf("%d\n",t.smax[root]);
}
return 0;
}
洛谷.2042.[NOI2005]维护数列(Splay)的更多相关文章
- 洛谷 P2042 [NOI2005]维护数列-Splay(插入 删除 修改 翻转 求和 最大的子序列)
因为要讲座,随便写一下,等讲完有时间好好写一篇splay的博客. 先直接上题目然后贴代码,具体讲解都写代码里了. 参考的博客等的链接都贴代码里了,有空再好好写. P2042 [NOI2005]维护数列 ...
- 洛谷P2042 [NOI2005]维护数列
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #in ...
- P2042 [NOI2005]维护数列 && Splay区间操作(四)
到这里 \(A\) 了这题, \(Splay\) 就能算入好门了吧. 今天是个特殊的日子, \(NOI\) 出成绩, 大佬 \(Cu\) 不敢相信这一切这么快, 一下子机房就只剩我和 \(zrs\) ...
- P2042 [NOI2005]维护数列[splay或非旋treap·毒瘤题]
P2042 [NOI2005]维护数列 数列区间和,最大子列和(必须不为空),支持翻转.修改值.插入删除. 练码力的题,很毒瘤.个人因为太菜了,对splay极其生疏,犯了大量错误,在此记录,望以后一定 ...
- BZOJ 1500 Luogu P2042 [NOI2005] 维护数列 (Splay)
手动博客搬家: 本文发表于20180825 00:34:49, 原地址https://blog.csdn.net/suncongbo/article/details/82027387 题目链接: (l ...
- NOI2005 维护数列(splay)
学了半天平衡树,选择了一道题来写一写,发现题目是裸的splay模板,但是还是写不好,这个的精髓之处在于在数列的某一个位置加入一个数列,类似于treap里面的merge,然后还学到了题解里面的的回收空间 ...
- 【洛谷P2042】维护数列
题目大意:维护一个序列,支持区间插入,区间删除,区间翻转,查询区间元素和,查询区间最大子段和操作. 题解:毒瘤题...QAQ打完这道题发现自己以前学了一个假的 Splay.. 对于区间操作,用 spl ...
- 数据结构(Splay平衡树):COGS 339. [NOI2005] 维护数列
339. [NOI2005] 维护数列 时间限制:3 s 内存限制:256 MB [问题描述] 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际 ...
- [NOI2005]维护数列(区间splay)
[NOI2005]维护数列(luogu) 打这玩意儿真是要了我的老命 Description 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文 ...
随机推荐
- 机器学习编程语言之争,Python夺魁
机器学习编程语言之争,Python夺魁 随着科技的发展,拥有高容量.高速度和多样性的大数据已经成为当今时代的主题词.数据科学领域中所采用的机器学习编程语言大相径庭.究竟哪种语言最适合机器学习成为争论不 ...
- nodejs的 new String
已知rwo4的记录中baitaiid是001// row4为jhlist开始循环结果 for(var i=0;i<row4.length;i++) { var baiTaiId=new Stri ...
- java并发编程系列二:原子操作/CAS
什么是原子操作 不可被中断的一个或者一系列操作 实现原子操作的方式 Java可以通过锁和循环CAS的方式实现原子操作 CAS( Compare And Swap ) 为什么要有CAS? Compar ...
- C++:greater<int>和less<int>
greater和less是xfunctional.h中的两个结构体,代码如下: template<class _Ty = void> struct less { // functor fo ...
- SHA算法:签名串SHA算法Java语言参考(SHAHelper.java)
SHAHelper.java package com.util; /** * @author wangxiangyu * @date:2017年10月16日 上午9:00:47 * 类说明:SHA签名 ...
- lvs基本概念、调度方法、ipvsadm命令及nat模型示例
LVS类型: NAT:-->(DNAT) DR TUN FULLNAT LVS的常见名词解释 CIP<-->VIP--DIP<-->RIP Direct Routing: ...
- ThinkPHP使用不当可能造成敏感信息泄露
ThinkPHP在开启DEBUG的情况下会在Runtime目录下生成日志,如果debug模式不关,可直接输入路径造成目录遍历. ThinkPHP3.2结构:Application\Runtime\Lo ...
- Oracle 相关概念
注:本文来源于 <腾科OCP培训课堂>.非准许商业活动. 标题:Oracle 相关概念 --->数据库名.实例名.数据库域名.全局数据名.服务名 一:数据库名 1:什么是数据库名 ...
- Linux 快速删除已输入的命令
从输入模式到命令模式: 按”:“到最后一行,再按ctrl+z 就好了 history 显示命令历史列表 ↑(Ctrl+p) 显示上一条命令 ↓(Ctrl+n) 显示下一条命令 !num 执行命令历史列 ...
- LeetCode(58): 最后一个单词的长度
Easy! 题目描述: 给定一个仅包含大小写字母和空格 ' ' 的字符串,返回其最后一个单词的长度. 如果不存在最后一个单词,请返回 0 . 说明:一个单词是指由字母组成,但不包含任何空格的字符串. ...