您的位置:首页 > 运维架构

维护数列 洛谷2042 bzoj1500 NOI2005

2018-04-02 13:16 351 查看
题意:

给你一个数列,你需要对它进行如下操作:

1.在某一个数后面插入连续的一些数

2.从某个数开始连续删除一些数

3.把从某个数开始的连续的一些数全部变为x

4.区间翻转

5.区间求和

6.区间最大子列

由于要区间翻转,所以建两个虚拟节点1号节点和n+2号节点,以便区间翻转。

插入连续的区间的话可以先把这个序列建成一棵平衡的树,再把k+1旋转到根,原来的k+2变为k+1的右子树,然后直接把新建好的树挂到k+2的左子树上就行了。

区间修改和区间翻转都是打标机,和线段树差不多,有区间修改的标记就没必要再执行区间翻转了。

求和和最大子列的维护方式也是和线段树差不多,就是除了左子树右子树,还要看这个节点本身的那个值对结果的影响。

最后就是,这个题的空间很小,要回收空间。

最后粘代码

//本题从洛谷神犇 I_AM_HelloWord 处学来 代码部分修改
#include <bits/stdc++.h>
using namespace std;

int n,m,fa[1000001],rt,cnt,a[1000001],id[1000001],c[1000001][2];
//a记录权值,id是节点编号
int sum[1000001],sz[1000001],v[1000001],mx[1000001],lx[1000001],rx[1000001];
//v是当前点的权值,mx是当前点的最大子列和,lx是左侧最大子列,rx是右侧
//注意:mx不可以为0,lx和rx是可以为0的,因为mx直接作为最终答案输出
queue <int> q;//记录可以被回收的点的id
int tag[1000001],rev[1000001];
void pushup(int x)
{
int l=c[x][0],r=c[x][1];
sum[x]=sum[l]+v[x]+sum[r];
sz[x]=sz[l]+sz[r]+1;
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]);
}
void build(int l,int r,int f)//f为当前点的父节点
{
int mid=(l+r)>>1,now=id[mid],pre=id[f];

if(l==r)
{
mx[now]=sum[now]=a[l];
tag[now]=rev[now]=0;
//这里这个tag和rev的清0是必要的,因为这个编号可能是之前删除过又拿出来用的
//虽然似乎清零过一遍了
lx[now]=rx[now]=max(a[l],0);
sz[now]=1;
}
if(l<mid)
build(l,mid-1,mid);
if(r>mid)
build(mid+1,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时同理
}
void pushdown(int x)
{
int l=c[x][0],r=c[x][1];
if(tag[x])
{
rev[x]=tag[x]=0;//我们有了一个统一修改的标记,再翻转就没有什么意义了
if(l)
{
tag[l]=1;
v[l]=v[x];
sum[l]=v[x]*sz[l];
}
if(r)
{
tag[r]=1;
v[r]=v[x];
sum[r]=v[x]*sz[r];
}
if(v[x]>=0)
{
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]=0;
mx[l]=v[x];
}
if(r)
{
lx[r]=rx[r]=0;
mx[r]=v[x];
}
}
}
if(rev[x])
{
rev[x]=0;
rev[l]^=1;
rev[r]^=1;
swap(lx[l],rx[l]);
swap(lx[r],rx[r]);
swap(c[l][0],c[l][1]);
swap(c[r][0],c[r][1]);
}
}
int find(int x,int rk)
{
pushdown(x);
int l=c[x][0],r=c[x][1];
if(sz[l]+1==rk)
return x;
if(sz[l]>=rk)
return find(l,rk);
else
return find(r,rk-sz[l]-1);
}
void rotate(int x,int &k)
{
int y=fa[x],z=fa[y],l,r;
if(c[y][0]==x)
l=0;
else
l=1;
r=l^1;
if(y==k)
k=x;//对k的真实改变
else if(c[z][0]==y)
c[z][0]=x;
else
c[z][1]=x;
fa[x]=z;
fa[y]=x;
fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
pushup(y);//注意顺序
pushup(x);
}
/*
inline void rotate(int x,int &k){
int y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1;
if (y==k)k=x;else c[z][c[z][1]==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);
//旋转操作,一定要上传记录标记
}*/
void splay(int x,int &k)//k是真实改变的
{
while(x!=k)
{
int y=fa[x],z=fa[y];
if(y!=k)
{
if(c[z][0]==y ^ c[y][0]==x)
rotate(x,k);
else
rotate(y,k);
}
rotate(x,k);
}
}
void insert(int k,int tot)
{
for(int i=1;i<=tot;i++)
scanf("%d",&a[i]);

a412
for(int i=1;i<=tot;i++)
{
if(!q.empty())
{
id[i]=q.front();
q.pop();
}
else
id[i]=++cnt;
}
build(1,tot,0);//将读入的tot个数建成一颗平衡树
int z=id[(1+tot)>>1];//取中点为根,让整棵树建好后尽可能平衡
int x=find(rt,k+1),y=find(rt,k+2);
//把k+1(注意我们已经右移了一个单位)和(k+1)+1移到根和右儿子
splay(x,rt);
splay(y,c[x][1]);
fa[z]=y;
c[y][0]=z;//直接把需要插入的这个平衡树的根挂到右儿子的左儿子上去就好了
pushup(y);
pushup(x);
}
//我们通过这个split操作,找到[k+1,k+tot],并把k,和k+tot+1移到根和右儿子的位置
//然后我们返回了这个右儿子的左儿子,这就是我们需要操作的区间
int split(int k,int tot)
{
int x=find(rt,k),y=find(rt,k+tot+1);
//find应该没错
// cout<<rt<<endl;
splay(x,rt);
splay(y,c[x][1]);
return c[y][0];
}
void recycle(int x)
{
int &l=c[x][0],&r=c[x][1];
if(l)
recycle(l);
if(r)
recycle(r);
q.push(x);
fa[x]=l=r=tag[x]=rev[x]=0;
}
void erase(int k,int tot)
{
int x=split(k,tot),y=fa[x];
recycle(x);
c[y][0]=0;//直接把这棵子树断开连接
pushup(y);
pushup(fa[y]);
}
void modify(int k,int tot,int val)
{
int x=split(k,tot),y=fa[x];
v[x]=val;
tag[x]=1;
sum[x]=sz[x]*val;
if(val>=0)
lx[x]=rx[x]=mx[x]=sum[x];
else
{
lx[x]=rx[x]=0;
mx[x]=v[x];
}
pushup(y);
pushup(fa[y]);
}
void rever(int k,int tot)
{
int x=split(k,tot),y=fa[x];
if(!tag[x])
{
rev[x]^=1;
swap(c[x][0],c[x][1]);
swap(lx[x],rx[x]);
pushup(y);
pushup(fa[y]);
}
}
//split错了
void query(int k,int tot)
{
int x=split(k,tot);
// cout<<x<<endl;
printf("%d\n",sum[x]);
}
int main()
{
scanf("%d%d",&n,&m);
mx[0]=a[1]=a[n+2]=-2e9;
for(int i=1;i<=n;i++)
scanf("%d",&a[i+1]);
//虚拟了两个节点1和n+2,然后把需要操作区间整体右移一个单位
for(int i=1;i<=n+2;i++)
id[i]=i;
build(1,n+2,0);//建树
rt=(n+3)>>1;
cnt=n+2;
int k,tot,val;
char s[10];
for(int i=1;i<=m;i++)
{
scanf("%s",s);
if(s[0]!='M'||s[2]!='X')
scanf("%d%d",&k,&tot);
if(s[0]=='I')
insert(k,tot);
if(s[0]=='D')
erase(k,tot);
if(s[0]=='M')
{
if(s[2]=='X')
printf("%d\n",mx[rt]);
else
{
scanf("%d",&val);
modify(k,tot,val);
}
}
if(s[0]=='R')
rever(k,tot);
if(s[0]=='G')
query(k,tot);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: