mex(线段树+离散化)

题目描述:

给你一个无限长的数组,初始的时候都为0,有3种操作:

操作1是把给定区间[l,r][l,r] 设为1,

操作2是把给定区间[l,r][l,r] 设为0,

操作3把给定区间[l,r][l,r] 0,1反转。

一共n个操作,每次操作后要输出最小位置的0。

题解:

经过分析观察,可以发现,答案只有可能是1,l,r+1

所以我们开一个数组记录1,以及所有的l,r,r+1,并离散化

然后用线段树模拟操作即可

这里有两种思路:

一种是记录某一区间内0的最小位置和1的最小位置,反转时互换两个位置

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int maxn=3e5+5;
int n,m;ll a[maxn];
struct que{
    int op;ll l,r;
}q[N];
int s0[maxn<<2],s1[maxn<<2],lazy[maxn<<2],rev[maxn<<2];
void pushup(int u){
    if(s0[u*2]) s0[u]=s0[u*2];
    else if(s0[u*2+1]) s0[u]=s0[u*2+1];
    else s0[u]=0;
    if(s1[u*2]) s1[u]=s1[u*2];
    else if(s1[u*2+1]) s1[u]=s1[u*2+1];
    else s1[u]=0;
}
void build(int u,int l,int r){
    rev[u]=0;lazy[u]=-1;
    if(l==r){
        s0[u]=l;s1[u]=0;
        return;
    }
    int mid=(l+r)/2;
    build(u*2,l,mid);
    build(u*2+1,mid+1,r);
    pushup(u);
}
void pushdown(int u,int l,int r){
    if(lazy[u]!=-1){
        lazy[u*2]=lazy[u];
        lazy[u*2+1]=lazy[u];
        rev[u*2]=rev[u*2+1]=0;
        int mid=(l+r)/2;
        if(lazy[u]==1){
            s0[u*2]=s0[u*2+1]=0;
            s1[u*2]=l,s1[u*2+1]=mid+1;
        } 
        else if(lazy[u]==0){
            s0[u*2]=l,s0[u*2+1]=mid+1;
            s1[u*2]=s1[u*2+1]=0;
        } 
        lazy[u]=-1;
    }
    if(rev[u]){
        if(lazy[u*2]!=-1) lazy[u*2]^=1;
        else rev[u*2]^=1;
        if(lazy[u*2+1]!=-1) lazy[u*2+1]^=1;
        else rev[u*2+1]^=1;
        swap(s0[u*2],s1[u*2]);
        swap(s0[u*2+1],s1[u*2+1]);
        rev[u]=0;
    }
}
void update(int u,int l,int r,int a,int b,int k){
    if(a<=l&&b>=r){
        lazy[u]=k;
        rev[u]=0;
        if(k==1) s0[u]=0,s1[u]=l;
        else if(k==0) s0[u]=l,s1[u]=0;
        return;
    }
    pushdown(u,l,r);
    int mid=(l+r)/2;
    if(a<=mid) update(u*2,l,mid,a,b,k);
    if(b>mid) update(u*2+1,mid+1,r,a,b,k);
    pushup(u);
}
void revere(int u,int l,int r,int a,int b){
    if(a<=l&&b>=r){
        if(lazy[u]==-1) rev[u]^=1;
        else lazy[u]^=1;
        swap(s0[u],s1[u]);
        return;
    }
    pushdown(u,l,r);
    int mid=(l+r)/2;
    if(a<=mid) revere(u*2,l,mid,a,b);
    if(b>mid) revere(u*2+1,mid+1,r,a,b);
    pushup(u);
}
int main(){
    scanf("%d",&m);
    a[++n]=1;
    for(int i=1;i<=m;i++){
        scanf("%d%lld%lld",&q[i].op,&q[i].l,&q[i].r);
        a[++n]=q[i].l;a[++n]=q[i].r;a[++n]=q[i].r+1;
    }
    sort(a+1,a+n+1);
    n=unique(a+1,a+1+n)-a-1;
    build(1,1,n);
    for(int i=1;i<=m;i++){
        if(q[i].op==1){
            int x=lower_bound(a+1,a+1+n,q[i].l)-a;
            int y=lower_bound(a+1,a+1+n,q[i].r)-a;
            update(1,1,n,x,y,1);
        }
        else if(q[i].op==2){
            int x=lower_bound(a+1,a+1+n,q[i].l)-a;
            int y=lower_bound(a+1,a+1+n,q[i].r)-a;
            update(1,1,n,x,y,0);
        }
        else{
            int x=lower_bound(a+1,a+1+n,q[i].l)-a;
            int y=lower_bound(a+1,a+1+n,q[i].r)-a;
            revere(1,1,n,x,y);
        }
        if(!s0[1]) printf("%lld\n",a[n]+1);
        else printf("%lld\n",a[s0[1]]);
    }
    return 0;
}

另一种是记录某一区间内1的个数,查询时如果一个区间内1的个数小于这个区间的总个数,则说明这个区间内有0,递归下去

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int maxn=3e5+5;
int n,m;ll a[maxn];
struct que{
    int op;ll l,r;
}q[N];
int sum[maxn<<2],lazy[maxn<<2],rev[maxn<<2];
void build(int u,int l,int r){
    rev[u]=0;lazy[u]=-1;sum[u]=0;//错误1:一开始只在l==r时赋了初值 
    if(l==r) return;
    int mid=(l+r)/2;
    build(u*2,l,mid);
    build(u*2+1,mid+1,r);
    sum[u]=sum[u*2]+sum[u*2+1];
}
void pushdown(int u,int l,int r){
    int mid=(l+r)>>1;
    if(lazy[u]!=-1){
        lazy[u<<1]=lazy[u];
        lazy[u<<1|1]=lazy[u];
        rev[u<<1]=0;
        rev[u<<1|1]=0;
        if(lazy[u]==1){
            sum[u<<1]=mid-l+1;
            sum[u<<1|1]=r-mid;
        }
        else{
            sum[u<<1]=0;
            sum[u<<1|1]=0;
        }
        lazy[u]=-1;
    }
    if(rev[u]){
        if(lazy[u<<1]!=-1) lazy[u<<1]^=1;
        else rev[u<<1]^=1;
        if(lazy[u<<1|1]!=-1) lazy[u<<1|1]^=1;
        else rev[u<<1|1]^=1;
        sum[u<<1]=mid-l+1-sum[u<<1];
        sum[u<<1|1]=r-mid-sum[u<<1|1];
        rev[u]=0;
    }
    //还是错误2 
}
void update(int u,int l,int r,int a,int b,int p){
    if(a<=l&&b>=r){
        if(l==r){
            if(p==1) sum[u]=1;
            else if(p==2) sum[u]=0;
            else sum[u]^=1;
        }
        else{
            if(p==1){
                lazy[u]=1;
                rev[u]=0;
                sum[u]=r-l+1;
            }
            else if(p==2){
                lazy[u]=0;
                rev[u]=0;
                sum[u]=0;
            }
            else{
                if(lazy[u]==-1) rev[u]^=1;
                else lazy[u]^=1;
                sum[u]=r-l+1-sum[u];
            }
            //错误2:lazy,rev是会相互影响的 
        }
        return;
    }
    pushdown(u,l,r);
    int mid=(l+r)/2;
    if(a<=mid) update(u*2,l,mid,a,b,p);
    if(b>mid) update(u*2+1,mid+1,r,a,b,p);
    sum[u]=sum[u*2]+sum[u*2+1];
}
ll query(int u,int l,int r){
    if(l==r) return a[l];
    pushdown(u,l,r);
    int mid=(l+r)>>1;
    if(sum[u<<1]<mid-l+1) return query(u<<1,l,mid);
    else return query(u<<1|1,mid+1,r);
}
int main(){
    scanf("%d",&m);
    a[++n]=1;
    for(int i=1;i<=m;i++){
        scanf("%d%lld%lld",&q[i].op,&q[i].l,&q[i].r);
        a[++n]=q[i].l;a[++n]=q[i].r;a[++n]=q[i].r+1;
    }
    sort(a+1,a+n+1);
    n=unique(a+1,a+1+n)-a-1;
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int x=lower_bound(a+1,a+1+n,q[i].l)-a;
        int y=lower_bound(a+1,a+1+n,q[i].r)-a;
        update(1,1,n,x,y,q[i].op);
        printf("%lld\n",query(1,1,n));
    }
    return 0;
}

 

posted @ 2019-08-19 18:21  Mistletoes  阅读(239)  评论(0编辑  收藏  举报