【bzoj1500】维修数列 splay

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1500

【题解】

这是一道很恶心的题,虽然差不多是splay的板子。

我们用mx[x]表示以x为根的子树内最大子串和,lx[x]表示以x为根的子树中以左端点为起点向右延伸的最大子串和,rx[x]表示从右端点向左延伸的最大子串和。

维护rx和lx的过程中要注意单个结点的情况:如果v[x]>=0,rx[x]=lx[x]=v[x],否则rx[x]=lx[x]=0.

坑点:mx[0]=-INF,以及维护过程中不能更新0号结点

没有想明白的地方:翻转标记提前下传的必要性,据Cydiater大神所说,如果不提前下传,在区间修改中会导致翻转标记丢失,但是不懂这是为什么。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define FILE "read"
#define MAXN 500010
#define INF 1000000000
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
namespace INIT{
	char buf[1<<15],*fs,*ft;
	inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
	inline int read(){
		int x=0,f=1;  char ch=getchar();
		while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
		while(isdigit(ch))  {x=(x<<3)+(x<<1)+ch-'0';  ch=getchar();}
		return x*f;
	}
}using namespace INIT;
int n,m,top,a[MAXN],stack[MAXN];
namespace Splay_Tree{
	int root,cnt,f[MAXN],id[MAXN],size[MAXN],sum[MAXN],tag[MAXN],mx[MAXN],v[MAXN],rx[MAXN],lx[MAXN],vis[MAXN],son[MAXN][2];
	bool get(int x){return son[f[x]][1]==x;}
	void updata(int x){
		int l=son[x][0],r=son[x][1];
		size[x]=size[l]+size[r]+1;  sum[x]=sum[l]+sum[r]+v[x]; 
		mx[x]=max(mx[l],mx[r]);  cmax(mx[x],rx[l]+v[x]+lx[r]); 
		rx[x]=max(rx[r],sum[r]+v[x]+rx[l]);  lx[x]=max(lx[l],sum[l]+v[x]+lx[r]); 
	}
	void rotate(int x){
		int y=f[x],z=f[y],which=get(x);
		son[y][which]=son[x][which^1];  f[son[y][which]]=y;
		son[x][which^1]=y;  f[y]=x;  f[x]=z;
		if(z)  son[z][son[z][1]==y]=x;
		updata(y);  updata(x);
	}
	void splay(int x,int k){
		for(int y;(y=f[x]);rotate(x)){
			if(x==k)  break;
			else if(y==k)  {rotate(x); break;}
			else if(f[y]==k)  {rotate(get(x)==get(y)?y:x);  rotate(x); break;}
			else rotate(get(x)==get(y)?y:x);
		}
		if(k==root)  root=x;
	}
	void pushdown(int x){
		int l=son[x][0],r=son[x][1];
		if(tag[x]){
			tag[x]=vis[x]=0;
			if(l) tag[l]=1,v[l]=v[x],sum[l]=size[l]*v[x],lx[l]=rx[l]=(v[x]>0?sum[l]:0),mx[l]=(v[x]>0?sum[l]:v[l]);
			if(r) tag[r]=1,v[r]=v[x],sum[r]=size[r]*v[x],lx[r]=rx[r]=(v[x]>0?sum[r]:0),mx[r]=(v[x]>0?sum[r]:v[r]);
		}
		if(vis[x]){
			vis[l]^=1;  vis[r]^=1;  vis[x]=0;
			swap(lx[l],rx[l]);  swap(lx[r],rx[r]);
			swap(son[l][0],son[l][1]);  swap(son[r][0],son[r][1]);
		}
	}
	void build(int l,int r,int p){
		if(l>r)  return;
		int mid=(l+r)>>1,x=id[mid],y=id[p];
		if(l==r){
			mx[x]=a[l];sum[x]=a[l];size[x]=1;
			tag[x]=vis[x]=0; lx[x]=rx[x]=(a[l]>0?a[l]:0);
		}
		else build(l,mid-1,mid);  build(mid+1,r,mid);
		v[x]=a[mid]; f[x]=y; son[y][mid>=p]=x; updata(x);  
	}
	int find(int x){
		int now=root;
		while(1){
			pushdown(now);
			int temp=size[son[now][0]];
			if(x<=temp)  now=son[now][0];
			else if(x==temp+1)  return now;
			else x-=temp+1,now=son[now][1];
		}
	}
	int split(int posi,int tot){
		int x=find(posi),y=find(posi+tot+1);
		splay(x,root);  splay(y,son[x][1]);
		return son[y][0];
	}
	void insert(int posi,int tot){
		up(i,1,tot)  a[i]=read();
		up(i,1,tot)  if(top) id[i]=stack[top--];
		else id[i]=++cnt;
		build(1,tot,0);  int z=id[(1+tot)>>1];
		int x=find(posi+1),y=find(posi+2);
		splay(x,root);  splay(y,son[x][1]);
		f[z]=y;  son[y][0]=z;
		updata(y);  updata(x);
	}
	void clear(int x){
		if(!x) return;
		clear(son[x][0]);  clear(son[x][1]);
		stack[++top]=x; son[x][0]=son[x][1]=f[x]=vis[x]=tag[x]=0;
	}
	void del(int posi,int tot){
		int x=split(posi,tot),y=f[x];  
		clear(x);son[y][0]=0;
		updata(y);  updata(f[y]);
	}
	void change(int posi,int tot,int c){
		int x=split(posi,tot),y=f[x];
		tag[x]=1; v[x]=c; sum[x]=size[x]*c;
		rx[x]=lx[x]=(c>0?sum[x]:0); mx[x]=(c>0?sum[x]:c);
		updata(y);  updata(f[y]);
	}
	void rever(int posi,int tot){
		int x=split(posi,tot),y=f[x];
		if(tag[x])  return;
		vis[x]^=1;
		swap(son[x][0],son[x][1]); swap(lx[x],rx[x]);
		updata(y);  updata(f[y]);
	}
	void query(int posi,int tot){
		int x=split(posi,tot); 
		printf("%d\n",sum[x]);
	}
}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	using namespace Splay_Tree;
	n=read();  m=read();  mx[0]=a[1]=a[n+2]=-INF;
	up(i,1,n)  a[i+1]=read();
	up(i,1,n+2)  id[i]=i;
	build(1,n+2,0);  root=(n+3)>>1;  cnt=n+2;
	up(i,1,m){
		char ch[15];  scanf("%s",ch+1);
		if(ch[1]=='I'){int posi=read(),tot=read(); insert(posi,tot);}
		else if(ch[1]=='D'){int posi=read(),tot=read();del(posi,tot);}
		else if(ch[1]=='M'){
			if(ch[3]=='K'){int posi=read(),tot=read(),c=read();change(posi,tot,c);}
			else{printf("%d\n",mx[root]);}
		}
		else if(ch[1]=='R'){int posi=read(),tot=read();rever(posi,tot);}
		else {int posi=read(),tot=read();query(posi,tot);}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值