CF817F MEX Queries

Link\text{Link}

题意

  • 维护一个 0101 序列 a11018+1a_{1\sim10^{18}+1},初始均为 00
  • nn 组询问,每组询问输入三个数 t,l,rt,l,r
    • t=1t=1:把 alra_{l\sim r} 赋值为 11
    • t=2t=2:把 alra_{l\sim r} 赋值为 00
    • t=3t=3:把 alra_{l\sim r} 取反。
  • 每次操作后输出一个数 xx,满足 ax=0a_x=0xx 最小。

分析

看到区间操作,显然第一时间可以想到线段树,对于 t=1,2t=1,2 的操作,维护一个标记 tag1{1,0,1}tag1\in\{-1,0,1\},表示 0011 表示把该区间复制为 tag1tag1tag1=1tag1=-1 表示没有标记,对于 t=3t=3 的操作,维护一个标记 tag2{0,1}tag2\in\{0,1\} 表示是否有翻转。

然而题目求的是总体的 MEX\text{MEX},我们要找到最小的值为 00 的位置,于是就可以维护一个值 full{0,1}full\in\{0,1\} 表示该区间是否全为 11,查询答案是从根节点往下找,如果左子树全为 11 那么答案就在右子树,否则在左子树。

然而这样并不好维护,经过翻转操作后无法确定 fullfull 的值,你不知道翻转前该区间是全为 00 还是有一点 11,因此我们可以再维护一个值 any{0,1}any\in\{0,1\} 表示区间中是否至少有一个 11,这样翻转某个区间的函数就很好写了:

void rev(int p){//翻转p节点
	a[p].tag2^=1;//翻转标记
	if(a[p].full)
		a[p].full=a[p].any=0;//之前全是1,翻转后全是0
	else{
		a[p].full=!a[p].any;//之前没有满,翻转后的结果随any取值而定 
		a[p].any=1;//由于翻转前没有满,所以翻转后一定有1 
	}
}

赋值时,把 fullfullanyany 都改成赋的值就好了。

void fuz(int p,int v){//赋值 
	a[p].any=a[p].full=a[p].tag1=v;
	a[p].tag2=0;//赋值后无所谓翻转 
}

相应的 pushup\text{pushup}

void pushup(int p){
    a[p].full=a[pl].full&&a[pr].full;
    a[p].any=a[pl].any||a[pr].any;
}

由于 l,rl,r 范围过大,我一开始使用的是动态开点的线段树,然而由于树太深,开点操作太多导致 TLE 了,于是又加上了离散化才过,可动态开点加上离散化后就没太大用处了。

关于离散化的提示:一定要有 11!定要有 11!要有 11!有 1111!!(手动回声

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
    long long x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=5e5+10;
#define pl a[p].tl
#define pr a[p].tr
#define DOP_S_T_T bool
struct DOP_Segment_Tree{
    int root,tot;
    struct Tree{
        ll l,r;
        int tl,tr;//左右子树 
        int tag1,tag2;
        DOP_S_T_T full,any;
    }a[N*4];
	void fuz(int p,int v){//赋值 
		a[p].any=a[p].full=a[p].tag1=v;
		a[p].tag2=0;//赋值后无所谓翻转 
	}
	void rev(int p){//翻转p节点
		a[p].tag2^=1;//翻转标记
		if(a[p].full)
			a[p].full=a[p].any=0;//之前全是1,翻转后全是0
		else{
			a[p].full=!a[p].any;//之前没有满,翻转后的结果随any取值而定 
			a[p].any=1;//由于翻转前没有满,所以翻转后一定有1 
		}
	}
	void pushup(int p){
	    a[p].full=a[pl].full&&a[pr].full;
	    a[p].any=a[pl].any||a[pr].any;
	}
    void pushdown(int p){
        if(a[p].tag1!=-1){
        	fuz(pl,a[p].tag1);
        	fuz(pr,a[p].tag1);
        	a[p].tag1=-1;
		}
		else if(a[p].tag2){
			if(a[pl].tag1==-1)
				rev(pl);
			else
				fuz(pl,!a[pl].tag1);
			if(a[pr].tag1==-1)
				rev(pr);
			else
				fuz(pr,!a[pr].tag1);
			a[p].tag2=0;
		}
    }
    void make(int p){//开点 
        if(!pl){
            ll mid=(a[p].l+a[p].r)>>1;
            a[pl=++tot].l=a[p].l;
            a[pl].r=mid;
            fuz(pl,0);
            a[pr=++tot].l=mid+1;
            a[pr].r=a[p].r;
            fuz(pr,0);
        }
    }
    void build(ll l,ll r){//建树 
        root=tot=1;
        a[root].l=l;
        a[root].r=r;
        fuz(root,0);
    }
    void change1(int p,ll l,ll r,DOP_S_T_T v){//赋值 
        if(l<=a[p].l&&a[p].r<=r){
            fuz(p,v);
            return;
        }
        make(p);
        pushdown(p);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            change1(pl,l,r,v);
        if(r>mid)
            change1(pr,l,r,v);
        pushup(p);
    }
    void change2(int p,ll l,ll r){//翻转 
        if(l<=a[p].l&&a[p].r<=r){
        	if(a[p].tag1!=-1) fuz(p,!a[p].tag1);
        	else rev(p);
            return;
        }
        make(p);
        pushdown(p);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            change2(pl,l,r);
        if(r>mid)
            change2(pr,l,r);
        pushup(p);
    }
    ll ask(int p){//查询 
        if(a[p].l==a[p].r)
            return a[p].l;
        make(p);
        pushdown(p);
        if(a[pl].full)
        	return ask(pr);
        return ask(pl);
    }
}tree;
int n,m;
ll t[N],l[N],r[N],a[4*N];
int main(){
	n=read();
	a[++m]=1;
	for(int i=1;i<=n;i++){
		t[i]=read();l[i]=read();r[i]=read();
		a[++m]=max(l[i]-1,1ll);a[++m]=l[i];
		a[++m]=r[i];a[++m]=r[i]+1;
	}
	sort(a+1,a+m+1);
	int tot=unique(a+1,a+m+1)-(a+1);
	tree.build(1,tot+10);
	for(int i=1;i<=n;i++){
		l[i]=lower_bound(a+1,a+tot+1,l[i])-a;
		r[i]=lower_bound(a+1,a+tot+1,r[i])-a;
		if(t[i]==1) tree.change1(1,l[i],r[i],1);
		if(t[i]==2) tree.change1(1,l[i],r[i],0);
		if(t[i]==3) tree.change2(1,l[i],r[i]);
		write(a[tree.ask(1)]);
		putchar('\n');
	}
    return 0;
}
posted @ 2021-11-29 16:13  luckydrawbox  阅读(9)  评论(0)    收藏  举报  来源