[NOI 2005]维护数列
[NOI 2005]维护数列
题意:
题解:
平衡树区间操作大礼包
以下是 fhqTreap 的
关于 fhq 的原理可以看(理解 split 和 merge 时自己多画点图)这个
fhq的区间操作很简单,和线段树是一样的,都是维护一些\(tag\)
如果你要对\([l,r]\)区间加
你就先\(split(root,l,x,y)\),再\(split(y,r-l+1,y,z)\)
再对\(y\)打一个\(tag_sum\),标记下传即可
回到这题
最大子段和用分治做,维护\(lmax,rmax,mmax\)表示从最左端起的最大子段和,最右端起的,整段的
转移看代码,很容易理解
复杂度:\(O(n\log n)\)
听说splay常数比较小,可是splay太长不想打
其实是我不会打
#include<bits/stdc++.h>
using namespace std;
#define maxn 500005
inline void read(int& x)
{
x=0;char c=getchar();int f=1;
while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=f;
}
int sta[maxn];
queue<int> rubbish;//垃圾回收
namespace fhqTreap
{
#define ls son[rt][0]
#define rs son[rt][1]
#define inf 0x3f3f3f3f
int son[maxn][2],siz[maxn],val[maxn],rnd[maxn],sum[maxn];//sum是区间和
int lmax[maxn],rmax[maxn],mmax[maxn],cov[maxn];//cov是区间覆盖的tag
bool lazy_reverse[maxn];//是否翻转tag
int root,tot,x,y,z;
void del(const int& rt)//删除掉这颗子树
{
if(ls) del(ls);
rubbish.push(rt);
if(rs) del(rs);
}
inline int newnode(int v)//新建节点
{
int rt;
if(rubbish.empty()) rt=++tot;
else rt=rubbish.front(),rubbish.pop();//如果垃圾桶里还有就用
siz[rt]=1;rnd[rt]=rand();
val[rt]=sum[rt]=lmax[rt]=mmax[rt]=rmax[rt]=v;
ls=rs=0;
cov[rt]=inf,lazy_reverse[rt]=0;
return rt;
}
inline void cover_reverse(int rt,int flag,int v)
//覆盖优先级大于翻转(因为你覆盖完了就没有翻转的必要了)
{
if(v!=inf)//cover
{
val[rt]=v,sum[rt]=v*siz[rt];
lmax[rt]=mmax[rt]=rmax[rt]=(v>0?sum[rt]:val[rt]);
cov[rt]=v;
flag=0;
}
if(flag)//reverse
{
lazy_reverse[rt]^=1;
swap(lmax[rt],rmax[rt]);
}
}
inline void pushup(int& rt)//上传更新父节点数据
{
if(!rt) return;
siz[rt]=1;
mmax[rt]=lmax[rt]=rmax[rt]=sum[rt]=val[rt];
if(ls)
{
mmax[rt]=max(max(mmax[rt],mmax[ls]),rmax[ls]+lmax[rt]);//子段和转移
lmax[rt]=max(lmax[ls],sum[ls]+lmax[rt]);
rmax[rt]=max(rmax[rt],rmax[ls]+sum[rt]);
sum[rt]+=sum[ls],siz[rt]+=siz[ls];
}
if(rs)
{
mmax[rt]=max(max(mmax[rt],mmax[rs]),lmax[rs]+rmax[rt]);
rmax[rt]=max(rmax[rs],sum[rs]+rmax[rt]);
lmax[rt]=max(lmax[rt],sum[rt]+lmax[rs]);
sum[rt]+=sum[rs],siz[rt]+=siz[rs];
}
}
inline void pushdown(int rt)//标记下传
{
#define rev lazy_reverse[rt]
if((!rev&&cov[rt]==inf)||!rt) return;
if(ls) cover_reverse(ls,rev,cov[rt]);
if(rs) cover_reverse(rs,rev,cov[rt]);
if(rev) swap(ls,rs);
rev=0,cov[rt]=inf;
#undef rev
}
void split(int rt,int k,int& x,int& y)//注意区间问题必须用rank分裂
{
if(!rt) x=y=0;
else
{
pushdown(rt);
if(k>siz[ls]) x=rt,split(rs,k-siz[ls]-1,rs,y);
else y=rt,split(ls,k,x,ls);
pushup(rt);
}
}
int merge(int x,int y)//注意如果你把0给上传或者下传了会发生一些奇怪的错误(我是RE)
{
if(!x&&!y) return 0;
if(!x) {pushdown(y);return y;}
if(!y) {pushdown(x);return x;}
pushdown(x),pushdown(y);
if(rnd[x]<rnd[y])
{
son[x][1]=merge(son[x][1],y);
pushup(x);
return x;
}
else
{
son[y][0]=merge(x,son[y][0]);
pushup(y);
return y;
}
}
inline int build(int a[],const int& l,const int& r)//笛卡尔树O(n)建树的方法
{
int top=0;
for(int i=l;i<=r;++i) a[i]=newnode(a[i]);
for(int i=l;i<=r;++i)
{
while(top&&rnd[a[i]]<rnd[sta[top]])
pushup(sta[top]),son[a[i]][0]=sta[top--];
if(top) son[sta[top]][1]=a[i];
sta[++top]=a[i];
}
while(top) pushup(sta[top--]);
return sta[1];
}
inline void insert(int a[],int pos,int len)
//连续插入一段数,先建好在直接merge进去。直接一个个插会TLE
{
y=build(a,1,len);
split(root,pos,x,z);
root=merge(merge(x,y),z);
}
inline void del(int pos,int len)//删除区间
{
split(root,pos-1,x,y);
split(y,len,y,z);
del(y);
root=merge(x,z);
}
inline void cover(int pos,int len,int v)//区间覆盖
{
split(root,pos-1,x,y);
split(y,len,y,z);
cover_reverse(y,0,v);
root=merge(merge(x,y),z);
}
inline void reverse(int pos,int len)//区间翻转
{
split(root,pos-1,x,y);
split(y,len,y,z);
cover_reverse(y,1,inf);
root=merge(merge(x,y),z);
}
inline int getsum(int pos,int len)//区间和
{
split(root,pos-1,x,y);
split(y,len,y,z);
int ans=sum[y];
root=merge(merge(x,y),z);
return ans;
}
inline int getmaxsum()//最大子段和
{
return mmax[root];
}
void print(int rt)//输出整棵树
{
if(ls) print(ls);
printf("%d ",val[rt]);
if(rs) print(rs);
}
}
using namespace fhqTreap;
int a[maxn];
int main()
{
//freopen("test.in","r",stdin);
srand(time(NULL));
int n,m,len,pos;
read(n),read(m);
for(int i=1;i<=n;++i) read(a[i]);
root=build(a,1,n);
//print(root);puts(" ");
char op[10];
while(m--)
{
scanf("%s",op);
if(op[0]=='M'&&op[5]=='U')
{
printf("%d\n",getmaxsum());
continue;
}
read(pos),read(len);
if(op[0]=='I')
{
for(int i=1;i<=len;++i) read(a[i]);
insert(a,pos,len);
}
else if(op[0]=='D') del(pos,len);
else if(op[0]=='M') read(a[1]),cover(pos,len,a[1]);
else if(op[0]=='R') reverse(pos,len);
else if(op[0]=='G') printf("%d\n",getsum(pos,len));
//print(root);puts(" ");
}
return 0;
}
另外,如果你只想维护序列而不需要排序,你可以
inline void insert(int v)
{
root=merge(root,newnode(v));
}
然后\(print(root)\)就能得到原序列
一切伟大的行动和思想,都有一个微不足道的开始。
There is a negligible beginning in all great action and thought.
There is a negligible beginning in all great action and thought.