bzoj1858 [Scoi2010]序列操作——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1858

线段树...调了一个上午...(后面带 // 的都是改出来的)

lazy 标记的下放好麻烦,还得考虑赋值和取反的先后顺序什么的...

因为在取反时把赋值标记 swap 了,所以下放的时候先判断取反再判断赋值...

而且WA了一上午的原因竟然是一开始不慎把取反以为成翻转了,后来没改干净...那个 rev 的名字啊...

总之没有太改变自己最初的想法、改了些细节就A了还是很高兴的!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=1e5+5;
int n,m,op,a,b,c[maxn];
struct N{
    int sum,z[3],y[3],m[3];
    int lz[3],rev,len;
}t[maxn<<2];
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
void pushup(int x)
{
    int ls=(x<<1),rs=(x<<1|1);
    t[x].sum=t[ls].sum+t[rs].sum;
    for(int i=0;i<=1;i++)
    {
        t[x].z[i]=t[ls].z[i]+(t[ls].z[i]==t[ls].len?t[rs].z[i]:0);
        t[x].y[i]=t[rs].y[i]+(t[rs].y[i]==t[rs].len?t[ls].y[i]:0);
//        t[x].m[i]=max(max(t[x].z[i],t[x].y[i]),t[ls].y[i]+t[rs].z[i]);//
        t[x].m[i]=max(max(t[ls].m[i],t[rs].m[i]),t[ls].y[i]+t[rs].z[i]);//
    }
}
void upt(int x,int val)//赋值 
{
    t[x].lz[val]=1;
    t[x].lz[!val]=0; t[x].rev=0;//!
    t[x].sum=t[x].len*val; 
    t[x].z[val]=t[x].y[val]=t[x].m[val]=t[x].len;
    t[x].z[!val]=t[x].y[!val]=t[x].m[!val]=0;
}
void re(int x)//取反 
{
    swap(t[x].z[0],t[x].z[1]); swap(t[x].y[1],t[x].y[0]);//!!!
    swap(t[x].m[1],t[x].m[0]);
    t[x].sum=t[x].len-t[x].sum; t[x].rev^=1;
    swap(t[x].lz[1],t[x].lz[0]);//!
}
void pushdown(int x)
{
//    if(t[x].len==1)return;//
    int ls=(x<<1),rs=(x<<1|1);
    if(t[x].rev)t[x].rev^=1,re(ls),re(rs);
    for(int v=0;v<=1;v++)
        if(t[x].lz[v])t[x].lz[v]=0,upt(ls,v),upt(rs,v);//顺序 
}
void build(int x,int l,int r)
{
    t[x].len=r-l+1;
    if(l==r)
    {
        t[x].z[c[l]]=t[x].y[c[l]]=t[x].m[c[l]]=1;
        t[x].sum=c[l]; //
        return;
    }
    int mid=((l+r)>>1);
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    pushup(x);
}
void update(int x,int l,int r,int L,int R,int val)
{
    if(l>=L&&r<=R)
    {
        upt(x,val);return;
    }
    pushdown(x);
    int mid=((l+r)>>1);
    if(mid>=L)update(x<<1,l,mid,L,R,val);
    if(mid<R)update(x<<1|1,mid+1,r,L,R,val);
    pushup(x);
}
void rever(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)
    {
        re(x);return;
    }
    int mid=((l+r)>>1);
    pushdown(x);
    if(mid>=L)rever(x<<1,l,mid,L,R);
    if(mid<R)rever(x<<1|1,mid+1,r,L,R);
    pushup(x);
}
int query(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return t[x].sum;
    int mid=((l+r)>>1),ret=0;
    pushdown(x);
    if(mid>=L)ret+=query(x<<1,l,mid,L,R);
    if(mid<R)ret+=query(x<<1|1,mid+1,r,L,R);
    return ret;
}
int ask(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return t[x].m[1];
    pushdown(x);//
    int mid=((l+r)>>1);
    if(mid>=R)return ask(x<<1,l,mid,L,R);
    if(mid<L)return ask(x<<1|1,mid+1,r,L,R);
    int ret=0;
    ret=max(ask(x<<1,l,mid,L,R),ask(x<<1|1,mid+1,r,L,R));
    ret=max(ret,min(t[x<<1].y[1],mid-L+1)+min(t[x<<1|1].z[1],R-mid));
    return ret;
}
int main()
{
    n=rd();m=rd();
    for(int i=1;i<=n;i++)c[i]=rd();
    build(1,1,n);
    while(m--)
    {
        op=rd(); a=rd()+1; b=rd()+1;
        if(op==0||op==1)update(1,1,n,a,b,op);
        if(op==2)rever(1,1,n,a,b);
        if(op==3)printf("%d\n",query(1,1,n,a,b));
        if(op==4)printf("%d\n",ask(1,1,n,a,b));
    }
    return 0;
} 

 

转载于:https://www.cnblogs.com/Zinn/p/9186579.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值