[SCOI2010]序列操作 (线段树区间合并)
地址:https://www.luogu.com.cn/problem/P2572
题意:
给一个序列,进行一下五种操作:
$1.$区间$[l,r]$全部变为$0$
$2.$区间$[l,r]$全部变为$1$
$3.$区间$[l,r]$内$0$全部变成$1$,$1$变成$0$
$4.$询问区间$[l,r]$内有多少个$1$
$5.$询问区间$[l,r]$内最多有多少个连续的$1$
思路:
首先,我们需要维护11个信息
$L:$区间左端点 $R:$区间右端点 $Sum:$区间和
$max[2]$:区间最大连续$0/1$的个数 $lmax[2]$:包含区间左端点的最大连续$0/1$个数 $rmax[2]$:包含区间右端点的最大连续$0/1$个数
$lazy$:区间覆盖信息,初始化为$-1$,表示区间未被覆盖 $rev:$ 区间翻转信息,表示区间是否应该翻转
现在考虑一下区间的合并$pushup$
区间合并时,主要考虑$lmax,rmax,max$这几个信息的合并
显然$t[rt].lmax=t[ls].max,t[rt].rmax=t[rs].max$
但是还要考虑左/右区间全为$0/1$的情况,如果有的话$t[rt].lmax+t[rs].lmax,t[rt].rmax+t[ls].rmax$
最后整个区间的最大值就为:左端点,右端点,合并后中间的最大值
再来考虑一下标记下沉$pushdown$,也是难点
对于标记下沉,通常我们应该两点:
$1.$标记的优先级
$2.$下放某一标记,是否会对其他标记产生影响
本题来说,区间覆盖的优先级高于区间翻转((因为先翻转再覆盖,翻转就会失去意义),在有区间覆盖标记的时候,应该将区间翻转标志直0
将区间赋值标记拆解时, 不仅需要将子区间赋值标记更新为此值, 还需要将子节点翻转标记清空
将区间翻转标记拆解时, 需要分两种情况考虑此标记下推对子区间 赋值标记 和 翻转标记造成的影响
考虑到赋值标记的优先级大于翻转标记, 在有赋值标记的情况下, 直接翻转赋值标记
其余情况翻转标记异或等于1
#include<iostream> #include<algorithm> #include<cstdio> #define lid (id<<1) #define rid (id<<1|1) #define REP(i, x, y) for(int i = (x);i <= (y);i++) using namespace std; const int maxn=1e5+10; struct node{ int l,r,sum,lazy,rev; int max[2],lmax[2],rmax[2]; }tree[maxn<<2]; int n,m,v[maxn]; void pushup(int id){ tree[id].sum = tree[lid].sum + tree[rid].sum; REP(i, 0, 1){ tree[id].lmax[i] = tree[lid].lmax[i]; if(i == 1 && tree[lid].sum == tree[lid].r - tree[lid].l + 1)//左区间全满 tree[id].lmax[i] += tree[rid].lmax[i];//可以跨越 else if(i == 0 && tree[lid].sum == 0)//左区间全空 tree[id].lmax[i] += tree[rid].lmax[i]; tree[id].rmax[i] = tree[rid].rmax[i]; if(i == 1 && tree[rid].sum == tree[rid].r - tree[rid].l + 1) tree[id].rmax[i] += tree[lid].rmax[i]; else if(i == 0 && tree[rid].sum == 0) tree[id].rmax[i] += tree[lid].rmax[i]; tree[id].max[i] = tree[lid].rmax[i] + tree[rid].lmax[i];//中间 tree[id].max[i] = max(tree[id].max[i], tree[lid].max[i]);//继承子区间 tree[id].max[i] = max(tree[id].max[i], tree[rid].max[i]); } } void build(int id, int l, int r){ tree[id].l = l, tree[id].r = r, tree[id].lazy = -1; if(l == r){ tree[id].sum = v[l]; tree[id].max[0] = tree[id].lmax[0] = tree[id].rmax[0] = v[l] == 0; tree[id].max[1] = tree[id].lmax[1] = tree[id].rmax[1] = v[l] == 1; return ; } int mid = (l + r) >> 1; build(lid, l, mid), build(rid, mid + 1, r); pushup(id); } void pushdown(int id){ if(tree[id].lazy != -1){//优先级最高 tree[id].rev = 0;//清空标记 int val = tree[id].lazy; tree[lid].sum = (tree[lid].r - tree[lid].l + 1) * val; tree[rid].sum = (tree[rid].r - tree[rid].l + 1) * val; tree[lid].lazy = tree[rid].lazy = val; tree[lid].rev = tree[rid].rev = 0; tree[lid].max[val] = tree[lid].lmax[val] = tree[lid].rmax[val] = tree[lid].r - tree[lid].l + 1; tree[lid].max[val ^ 1] = tree[lid].lmax[val ^ 1] = tree[lid].rmax[val ^ 1] = 0; tree[rid].max[val] = tree[rid].lmax[val] = tree[rid].rmax[val] = tree[rid].r - tree[rid].l + 1; tree[rid].max[val ^ 1] = tree[rid].lmax[val ^ 1] = tree[rid].rmax[val ^ 1] = 0; tree[id].lazy = -1; } if(tree[id].rev){ tree[lid].sum = (tree[lid].r - tree[lid].l + 1) - tree[lid].sum; tree[rid].sum = (tree[rid].r - tree[rid].l + 1) - tree[rid].sum; if(tree[lid].lazy != -1)tree[lid].lazy ^= 1;//综合考虑优先级, 对其他标记的影响 else tree[lid].rev ^= 1; if(tree[rid].lazy != -1)tree[rid].lazy ^= 1; else tree[rid].rev ^= 1; swap(tree[lid].max[0], tree[lid].max[1]); swap(tree[lid].lmax[0], tree[lid].lmax[1]); swap(tree[lid].rmax[0], tree[lid].rmax[1]); swap(tree[rid].max[0], tree[rid].max[1]); swap(tree[rid].lmax[0], tree[rid].lmax[1]); swap(tree[rid].rmax[0], tree[rid].rmax[1]); tree[id].rev = 0; } } void update(int id, int val, int l, int r){ pushdown(id); if(tree[id].l == l && tree[id].r == r){ if(val == 0 || val == 1){ tree[id].sum = (tree[id].r - tree[id].l + 1) * val; tree[id].lazy = val; tree[id].max[val] = tree[id].lmax[val] = tree[id].rmax[val] = tree[id].r - tree[id].l + 1; tree[id].max[val ^ 1] = tree[id].lmax[val ^ 1] = tree[id].rmax[val ^ 1] = 0; } else if(val == 2){ tree[id].sum = (tree[id].r - tree[id].l + 1) - tree[id].sum; tree[id].rev ^= 1; swap(tree[id].max[0], tree[id].max[1]); swap(tree[id].lmax[0], tree[id].lmax[1]); swap(tree[id].rmax[0], tree[id].rmax[1]); } return ; } int mid = (tree[id].l + tree[id].r) >> 1; if(mid < l)update(rid, val, l, r); else if(mid >= r)update(lid, val, l, r); else update(lid, val, l, mid), update(rid, val, mid + 1, r); pushup(id); } int query(int id, int l, int r){ pushdown(id); if(tree[id].l == l && tree[id].r == r)return tree[id].sum; int mid = (tree[id].l + tree[id].r) >> 1; if(mid < l)return query(rid, l, r); else if(mid >= r)return query(lid, l, r); else return query(lid, l, mid) + query(rid, mid + 1, r); } node Q_max(int id, int l, int r){ pushdown(id); if(tree[id].l == l && tree[id].r == r)return tree[id]; int mid = (tree[id].l + tree[id].r) >> 1; if(mid < l)return Q_max(rid, l, r); else if(mid >= r)return Q_max(lid, l, r); else{ node ret, L = Q_max(lid, l, mid), R = Q_max(rid, mid + 1, r); ret.sum = L.sum + R.sum; REP(i, 0, 1){ ret.lmax[i] = L.lmax[i]; if(i == 1 && L.sum == L.r - L.l + 1)//左区间全满 ret.lmax[i] += R.lmax[i];//可以跨越 else if(i == 0 && L.sum == 0)//左区间全空 ret.lmax[i] += R.lmax[i]; ret.rmax[i] = R.rmax[i]; if(i == 1 && R.sum == R.r - R.l + 1) ret.rmax[i] += L.rmax[i]; else if(i == 0 && R.sum == 0) ret.rmax[i] += L.rmax[i]; ret.max[i] = L.rmax[i] + R.lmax[i];//中间 ret.max[i] = max(ret.max[i], L.max[i]);//继承子区间 ret.max[i] = max(ret.max[i], R.max[i]); } return ret; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&v[i]); build(1,1,n); while(m--){ int op,l,r; scanf("%d%d%d",&op,&l,&r); l++,r++; if(op==0) update(1,0,l,r); else if(op==1) update(1,1,l,r); else if(op==2) update(1,2,l,r); else if(op==3) cout<<query(1,l,r)<<endl; else cout<<Q_max(1,l,r).max[1]<<endl; } return 0; }