联[线段树]

巧了我就是萌 提交于 2019-12-01 10:01:42

题意

范围$1e18$

题解

首先看到范围这么大,肯定要离散化的,

发现最后最左面0位置只可能修改过的位置-1,+1,等,不可能在一段区间中间凭空出现

于是可以这样离散化

        if(l[i]!=1)
            lsh[++lsh[0]]=l[i]-1;
        lsh[++lsh[0]]=l[i];
        lsh[++lsh[0]]=r[i];
        lsh[++lsh[0]]=r[i]+1;

然后考虑如何维护

两种方法:

1.维护最左0出现位置,最左1出现位置

这样异或操作就swap一下就好了

我没打这种

2.维护区间0个数,

这样异或操作就转化成了区间长度-0个数

查询如果左子树有0优先走左子树,否则走右子树,递归到叶子

3.keduoli树水过

这里用的第2种方法

两种懒标记不可同时存在,

down

void down(ll x){
//    printf("l=%lld r=%lld tr[lson].f1=%lld tr[rson].f1=%lld\n",tr[x].l,tr[x].r,tr[x<<1].f1,tr[x<<1|1].f1);
    if(tr[x].f1){
        if(tr[x<<1].f2){
            tr[x<<1].f2=0;
            tr[x<<1].sum=tr[x<<1].len-tr[x<<1].sum;
        }
        if(tr[x<<1|1].f2){
            tr[x<<1|1].f2=0;
            tr[x<<1|1].sum=tr[x<<1|1].len-tr[x<<1|1].sum;
        }
        tr[x<<1].f1=tr[x].f1;
        tr[x<<1].sum=((tr[x].f1==2)?tr[x<<1].len:0);
        tr[x<<1|1].f1=tr[x].f1;
        tr[x<<1|1].sum=((tr[x].f1==2)?tr[x<<1|1].len:0);
    }
    else if(tr[x].f2){//区间异或
        if(tr[x<<1].f2){
            tr[x<<1].f2=0;
            tr[x<<1].sum=tr[x<<1].len-tr[x<<1].sum;
        }
        else if(tr[x<<1].f1==1){
            tr[x<<1].sum=tr[x<<1].len;
            tr[x<<1].f1=2;
            tr[x<<1].f2=0;
        }
        else if(tr[x<<1].f1==2){
            tr[x<<1].sum=0;
            tr[x<<1].f1=1;
            tr[x<<1].f2=0;
        }
        else tr[x<<1].f2=1,tr[x<<1].sum=tr[x<<1].len-tr[x<<1].sum;
        if(tr[x<<1|1].f2){
            tr[x<<1|1].f2=0;
            tr[x<<1|1].sum=tr[x<<1|1].len-tr[x<<1|1].sum;
        }
        else if(tr[x<<1|1].f1==1){
            tr[x<<1|1].sum=tr[x<<1|1].len;
            tr[x<<1|1].f1=2;
            tr[x<<1|1].f2=0;
        }
        else if(tr[x<<1|1].f1==2){
            tr[x<<1|1].sum=0;
            tr[x<<1|1].f1=1;
            tr[x<<1|1].f2=0;
        }
        else tr[x<<1|1].f2=1,tr[x<<1|1].sum=tr[x<<1|1].len-tr[x<<1|1].sum;
    }    
//    printf("down*** f1=%lld f2=%lld lson.f2=%lld rson.f2=%lld  l=%lld r=%lld tr[x].sum=%lld lson l=%lld r=%lld sum=%lld rson l=%lld r=%lld sum=%lld\n",tr[x].f1,tr[x].f2,tr[x<<1].f2,tr[x<<1|1].f2,tr[x].l,tr[x].r,tr[x].sum,tr[x<<1].l,tr[x<<1].r,tr[x<<1].sum,tr[x<<1|1].l,tr[x<<1|1].r,tr[x<<1|1].sum);
//    printf("l=%lld r=%lld lson.f1=%lld f2=%lld sum=%lld l=%lld r=%lld rson.f1=%lld f2=%lld sum=%lld\n",tr[x<<1].l,tr[x<<1].r,tr[x<<1].f1,tr[x<<1].f2,tr[x<<1].sum,tr[x<<1|1].l,tr[x<<1|1].r,tr[x<<1|1].f1,tr[x<<1|1].f2,tr[x<<1|1].sum);
    tr[x].f1=0;
    tr[x].f2=0;

}

注意,这里区间修改因为两种懒标记不可同时存在,所以也要进行这种操作

可能两次都修改同一个区间,这样你上一个标记没下传,这一个标记就又来了

特殊处理一下

代码

void seg_add(ll x,ll l,ll r,ll val){
//    printf("l=%lld r=%lld tr[x].l=%lld tr[x].r=%lld\n",l,r,tr[x].l,tr[x].r);
    
    if(tr[x].l>=l&&tr[x].r<=r){
        if(val==3){//异或
            if(tr[x].f2){
                tr[x].f2=0;
                tr[x].sum=tr[x].len-tr[x].sum;
            }
            else if(tr[x].f1==1){
                tr[x].sum=tr[x].len;
                tr[x].f1=2;
                tr[x].f2=0;
            }
            else if(tr[x].f1==2){
                tr[x].sum=0;
                tr[x].f1=1;
                tr[x].f2=0;
            }
            else tr[x].f2=1,tr[x].sum=tr[x].len-tr[x].sum;
        }
        else if(val==1){//区间赋值1
            tr[x].sum=0;
            tr[x].f1=1;
            tr[x].f2=0;
        }
        else if(val==2){
            tr[x].sum=tr[x].len;
            tr[x].f1=2;
            tr[x].f2=0;
        }
//        printf("havefindit l=%lld r=%lld val=%lld val=%lld\n",tr[x].l,tr[x].r,tr[x].f1,val);
        return ;
    }
    if(tr[x].f1||tr[x].f2) down(x);
    ll mid=(tr[x].l+tr[x].r)>>1;
    if(mid>=l)seg_add(x<<1,l,r,val);
    if(mid<r) seg_add(x<<1|1,l,r,val);
    up(x);
}
View Code

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!