题目链接:点击打开链接
题目大意:
题目给你一段随机的01串,有五种操作。
(1):将某段区间的数全部变为0
(2):将某段区间的数全部变为1
(3):将某段区间的0变为1,1变为0
(4):统计当前区间的1的个数
(5):统计当前区间的最长连续1的个数
解题思路:
看到这道题,仿佛看到了自己目前做的所有线段树区间更新的影子。首先先分析查询,查询1的个数,这个肯定就要统计当前区间内所有1的个数。
还要求区间内连续1的最长长度,那么就是左最长右最长区间最长进行区间更新就行。刚开始我还维护了0的情况,因为考虑到翻转,但是写完之后发现维护的关于0的这些值并没有什么用,因为更新有点特殊。
接下来就是更新,更新显而易见操作1和操作2是可以打延迟标记的,但是操作3呢?虽然听说取反直接更新到子节点貌似可以卡时间过,但是这里还是说下正确的方法,
其实思路和HDU4578很像,也是因为刚做那道题所以这道题很快想出了更新思路。那就是有前提条件的延迟更新(我自己瞎说的)
操作3如果在当前区间已经进行过操作1和2的情况下,就可以延迟更新,因为节点会有个值存当前应该将整个区间赋为哪个数。那么如果进行过操作1或2的话,就直接将值取反就可以,不影响延迟更新,否则就更新到子节点,一定要想通这一点,剩下的就是正常的线段树操作了,
这道题一定要思路清晰再写,写了以后想再练相似的推荐HDU4578,wa了心平气和仔细找bug,反正我是一遍AC的(逃,
具体的话可以看代码,
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <functional>
#define rank ra
#define lson rt<<1
#define rson rt<<1|1
#define pb push_back
using namespace std;
typedef long long ll;
int n,m,ans;
int a[100010];
struct node
{
int l,r,mid;
int sum1; //记录当前区间1的值
int l1,r1,m1; //记录左最长右最长区间最长
int cnt; //记录当前区间应该被赋为0还是1
int lazy; //延迟标记
}t[500000];
void pushdown(int rt)
{
if(t[rt].lazy)
{
if(t[rt].cnt) //如果为1更新一系列操作
{
t[lson].m1=t[lson].r-t[lson].l+1;
t[lson].l1=t[lson].m1;t[lson].r1=t[lson].m1;
t[lson].sum1=t[lson].m1;
t[rson].m1=t[rson].r-t[rson].l+1;
t[rson].l1=t[rson].m1;t[rson].r1=t[rson].m1;
t[rson].sum1=t[rson].m1;
}
else //为0同上
{
t[lson].m1=0;t[lson].l1=0;t[lson].r1=0;
t[lson].sum1=0;
t[rson].m1=0;t[rson].l1=0;t[rson].r1=0;
t[rson].sum1=0;
}
t[lson].lazy=t[rson].lazy=1;
t[lson].cnt=t[rson].cnt=t[rt].cnt;
t[rt].lazy=0;
}
}
void pushup(int rt) //自下往上区间更新
{
t[rt].sum1=t[lson].sum1+t[rson].sum1;
if(t[lson].l1==t[lson].r-t[lson].l+1)
t[rt].l1=t[lson].l1+t[rson].l1;
else
t[rt].l1=t[lson].l1;
if(t[rson].r1==t[rson].r-t[rson].l+1)
t[rt].r1=t[lson].r1+t[rson].r1;
else
t[rt].r1=t[rson].r1;
t[rt].m1=max(max(t[lson].m1,t[rson].m1),t[lson].r1+t[rson].l1);
}
void build(int l,int r,int rt)
{
int mid=(l+r)>>1;
t[rt].l=l;t[rt].r=r;
t[rt].mid=mid;
t[rt].lazy=0;
t[rt].cnt=0;
if(l==r)
{
if(a[t[rt].l]) //根据1,0更新情况
{
t[rt].sum1=1;
t[rt].l1=1;t[rt].r1=1;t[rt].m1=1;
}
else
{
t[rt].sum1=0;
t[rt].l1=0;t[rt].r1=0;t[rt].m1=0;
}
return ;
}
build(l,mid,lson);
build(mid+1,r,rson);
pushup(rt);
}
void update(int l,int r,int flag,int rt)//如果是操作1 2或者是操作3
{ //但是当前进行过操作1 2被打了延迟标记
if(l<=t[rt].l&&t[rt].r<=r&&((flag==0||flag==1)||(t[rt].lazy==1&&flag==2)))
{
if(flag==0) //正常延迟更新
{
t[rt].lazy=1;
t[rt].cnt=0;
}
if(flag==1)
{
t[rt].lazy=1;
t[rt].cnt=1;
}
if(flag==2) //取反
t[rt].cnt^=1;
if(t[rt].cnt) //根据cnt值进行更新操作
{
t[rt].m1=t[rt].r-t[rt].l+1;
t[rt].l1=t[rt].m1;t[rt].r1=t[rt].m1;
t[rt].sum1=t[rt].m1;
}
else
{
t[rt].m1=0;t[rt].l1=0;t[rt].r1=0;
t[rt].sum1=0;
}
return ;
}
if(l<=t[rt].l&&t[rt].r<=r&&t[rt].l==t[rt].r&&flag==2)
{
t[rt].sum1^=1; //操作3更新到子节点取反即可
t[rt].l1^=1;t[rt].r1^=1;t[rt].m1^=1;
return ;
}
pushdown(rt);
if(l<=t[rt].mid)
update(l,r,flag,lson);
if(r>t[rt].mid)
update(l,r,flag,rson);
pushup(rt);
}
void query1(int l,int r,int rt) //查询1的个数
{
if(l<=t[rt].l&&t[rt].r<=r)
{
ans += t[rt].sum1;
return ;
}
pushdown(rt);
if(l<=t[rt].mid)
query1(l,r,lson);
if(r>t[rt].mid)
query1(l,r,rson);
pushup(rt);
}
int query2(int l,int r,int rt) //查询最长连续1的长度
{
if(l==t[rt].l&&r==t[rt].r)
return t[rt].m1;
pushdown(rt);
if(r<=t[rt].mid)
return query2(l,r,lson);
else if(l>t[rt].mid)
return query2(l,r,rson);
else
{
int aa=query2(l,t[rt].mid,lson);
int bb=query2(t[rt].mid+1,r,rson);
int cc=0;
cc=min(t[lson].r1,t[rt].mid-l+1)+min(t[rson].l1,r-t[rt].mid);
return max(max(aa,bb),cc);
}
pushup(rt);
}
int main()
{
int QAQ;
scanf("%d",&QAQ);
while(QAQ--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
int q,l,r;
while(m--)
{
scanf("%d%d%d",&q,&l,&r);
if(q>=0&&q<=2)
update(l+1,r+1,q,1); //注意加1 刚上来还纳闷为啥样例没过
if(q==3)
{
ans=0;
query1(l+1,r+1,1);
printf("%d\n",ans);
}
if(q==4)
{
ans=query2(l+1,r+1,1);
printf("%d\n",ans);
}
}
}
}