BZOJ_1251_序列终结者
BZOJ_1251_序列终结者
【问题描述】 给定一个长度为N的序列,每个序列的元素是一个整数(废话)。要支持以下三种操作: 1. 将[L,R]这个区间内的所有数加上V。 2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。 3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。
分析:模板题
维护两个标记即可,标记只对子树生效
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 50050 #define ls ch[p][0] #define rs ch[p][1] int n,m; int ch[N][2],f[N],siz[N],rt; int turn[N],del[N],val[N],mx[N]; int get(int x){ return ch[f[x]][1]==x; } void up(int p) { if(p)siz[p]=siz[ls]+siz[rs]+1; mx[p]=max(mx[ls],mx[rs]); mx[p]=max(mx[p],val[p]); } void rotate(int x) { int y=f[x],z=f[y],k=get(x); ch[y][k]=ch[x][k^1];f[ch[y][k]]=y;ch[x][k^1]=y;f[y]=x;f[x]=z; if(z) ch[z][ch[z][1]==y]=x; up(y),up(x); if(rt==y)rt=x; } void splay(int x,int y) { for(int fa;(fa=f[x])!=y;rotate(x)) if(f[fa]!=y) rotate((get(x)==get(fa)) ? fa : x); } void pud(int p) { int t=del[p]; if(t) { del[p]=0; if(ls)del[ls]+=t, mx[ls]+=t, val[ls]+=t; if(rs)del[rs]+=t, mx[rs]+=t, val[rs]+=t; } if(turn[p]) { turn[p]=0; swap(ls,rs); turn[ls]^=1;turn[rs]^=1; } //up(p); } void build(int fa,int l,int r) { if(l>r)return; int mid=l+r>>1; ch[fa][mid>fa]=mid; siz[mid]=1; val[mid]=0; f[mid]=fa; build(mid,l,mid-1); build(mid,mid+1,r); up(mid); } int find(int x) { int p=rt; while(1) { pud(p); if(x<=siz[ls])p=ls; else { x-=siz[ls]+1; if(!x)return p; p=rs; } } } void reverse(int x,int p) { x=find(x); p=find(p); //up(x);up(p); splay(x,0); splay(p,rt); //up(x);up(p); turn[ls]^=1; } void add(int x,int p,int c) { x=find(x); p=find(p); splay(x,f[rt]); splay(p,rt); del[ls]+=c; val[ls]+=c; mx[ls]+=c; //up(p); //up(x); } void qmx(int x,int p) { x=find(x); p=find(p); splay(x,f[rt]); splay(p,rt); printf("%d\n",mx[ls]); } int main() { scanf("%d%d",&n,&m); int i,opt,x,y,z; for(i=0;i<=n+2;i++)mx[i]=-100000000; build(0,1,n+2); rt=n+3>>1; for(i=1;i<=m;i++) { scanf("%d",&opt); if(opt==1) { scanf("%d%d%d",&x,&y,&z); if(x>y)swap(x,y); add(x,y+2,z); } else if(opt==2) { scanf("%d%d",&x,&y); if(x>y)swap(x,y); reverse(x,y+2); } else { scanf("%d%d",&x,&y); if(x>y)swap(x,y); qmx(x,y+2); } } }