mex(线段树+离散化)

Posted harrypotter-fan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;

 

以上是关于mex(线段树+离散化)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ.3585.mex(线段树)

线段树+区间离散化

浅谈线段树离散化

线段树离散化

贴海报 (线段树染色-离散化

POJ - 2528 线段树+离散化