P2572 [SCOI2010] 序列操作
分析
这题,论码量在树剖+线段树面前肯定是不够看的,但是单论线段树部分,码量不小,请做好心理准备。
此题需要我们进行的操作有
0 l r把 [l, r] 区间内的所有数全变成 01 l r把 [l, r] 区间内的所有数全变成 12 l r把 [l,r] 区间内的所有数全部取反,也就是说把所有的 0 变成 1,把所有的 1 变成 03 l r询问 [l, r] 区间内总共有多少个 14 l r询问 [l, r] 区间内最多有多少个连续的 1
我们还是老规矩,根据操作,来看我们线段树中需要维护什么。
前三个操作,需要我们维护两个懒标记
cov,其中-1表示无区间覆盖操作,0/1表示区间覆盖0/1rev,0表示无区间反转操作,1表示有区间反转操作
第四个操作,维护一个sum表示即可。
重点说一下第五个。
第五个操作,求有多少连续的1,那我们自然就联想到求最大子段和的线段树了呀。
那我们维护的东西自然也就得到了。
lmx[2],其中lmx[0/1]表示从左起最多的连续0/1的个数rmx[2],其中rmx[0/1]表示从右起最多的连续0/1的个数mx[2],其中mx[0/1]表示区间中最多的连续0/1的个数
这题就考察了对pushdown操作的了解。
对不同的懒标记,pushdown对其余的懒标记的影响,以及对其余变量的影响
这点很关键,尤其容易忘记对其余懒标记的影响。
因为不同懒标记是会相互影响的,一般是有优先级的,因此我们一定要注意,pushdown时,对其余懒标记的操作
我个人的经验是,不论是pushup还是pushdown,其对变量的影响是一样的,因此当影响很多的时候,单独写成一个函数可以有效避免错误
其余的,我们来看代码说话吧。
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct Node
{
int l,r,sum;
int lmx[2],rmx[2],mx[2];
int cov,rev;
}tr[N<<2];
int w[N];
int n,m;
void work(Node &u,Node &l,Node &r,int f)//维护分别对于0,1而言的lmx,rmx,mx
{
if(l.lmx[f]==l.r-l.l+1) u.lmx[f] = l.r - l.l + 1 + r.lmx[f];
else u.lmx[f] = l.lmx[f];
if(r.rmx[f]==r.r-r.l+1) u.rmx[f] = r.r - r.l + 1 + l.rmx[f];
else u.rmx[f] = r.rmx[f];
u.mx[f] = max(max(l.mx[f],r.mx[f]),l.rmx[f]+r.lmx[f]);
}
void push(Node &u,Node &l,Node &r)
{
u.sum = l.sum + r.sum;
work(u,l,r,0);
work(u,l,r,1);
}
void pushup(int u)
{
push(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
tr[u] = {l,r};
tr[u].cov = -1;//全部都要初始化为-1
if(l==r)
{
tr[u].sum = w[l];//到叶节点后初始化
tr[u].lmx[w[l]] = tr[u].rmx[w[l]] = tr[u].mx[w[l]] = 1;
return ;
}
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void changecov(Node &u,int k)//pushdown操作中,cov标记对区间的影响
{
u.lmx[k] = u.rmx[k] = u.mx[k] = u.r - u.l + 1;
u.lmx[k^1] = u.rmx[k^1] = u.mx[k^1] = 0;
u.sum = (u.r - u.l + 1)*k;
u.rev = 0;//记得要将翻转操作的懒标记清空
u.cov = k;
}
void changerev(Node &u)//pushdown操作中,rev标记对区间的影响,即0,1维护的值翻转
{
swap(u.lmx[0],u.lmx[1]);swap(u.rmx[0],u.rmx[1]);swap(u.mx[0],u.mx[1]);
u.sum = u.r - u.l + 1 - u.sum;
u.rev ^= 1;
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
if(root.cov!=-1)//注意优先级,我们先处理,区间覆盖操作,原因在modfiy操作中会说
{
auto k = root.cov;
changecov(left,k);
changecov(right,k);
root.cov = -1;
}
if(root.rev)
{
changerev(left);
changerev(right);
root.rev = 0;
}
}
void modify(int u,int l,int r,int k)
{
if(l<=tr[u].l&&tr[u].r<=r)//区间覆盖操作会直接抹掉之前的一切翻转操作,因此我们先进行区间覆盖
{
if(k!=2)
{
changecov(tr[u],k);
return ;
}
changerev(tr[u]);
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
Node query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if(l>mid) return query(u<<1|1,l,r);
else if(r<=mid) return query(u<<1,l,r);
else
{
Node left = query(u<<1,l,r),right = query(u<<1|1,l,r);
Node res;
push(res,left,right);
return res;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",w+i);
build(1,1,n);
while(m--)
{
int op,l,r;scanf("%d%d%d",&op,&l,&r);
++l,++r;
if(!op) modify(1,l,r,0);
else if(op==1) modify(1,l,r,1);
else if(op==2) modify(1,l,r,2);
else if(op==3) printf("%d\n",query(1,l,r).sum);
else printf("%d\n",query(1,l,r).mx[1]);
}
return 0;
}

浙公网安备 33010602011771号