CF817F MEX Queries
题目描述
You are given a set of integer numbers, initially it is empty. You should perform nn queries.
There are three different types of queries:
- 1 ll rr — Add all missing numbers from the interval [l,r][l,r]
- 2 ll rr — Remove all present numbers from the interval [l,r][l,r]
- 3 ll rr — Invert the interval [l,r][l,r] — add all missing and remove all present numbers from the interval [l,r][l,r]
After each query you should output MEX of the set — the smallest positive (MEX >=1>=1 ) integer number which is not presented in the set.
输入格式
The first line contains one integer number nn ( 1<=n<=10^{5}1<=n<=105 ).
Next nn lines contain three integer numbers t,l,rt,l,r ( 1<=t<=3,1<=l<=r<=10^{18}1<=t<=3,1<=l<=r<=1018 ) — type of the query, left and right bounds.
输出格式
Print MEX of the set after each query.
题意翻译
-
维护一个集合,初始为空。
-
有
33
种操作:
- 把 [l,r][l,r] 中在集合中没有出现过的数添加到集合中。
- 把 [l,r][l,r] 中在集合中出现过的数从集合中删掉。
- 把 [l,r][l,r] 中在集合中没有出现过的数添加到集合中,并把 [l,r][l,r] 中在集合中出现过的数从集合中删掉。
-
每次操作后输出集合的 operatorname{MEX}MEX —— 在集合中没有出现过的最小正整数。
-
1le nle 10^51≤n≤105,1le lle rle 10^{18}1≤l≤r≤1018。
题解:
线段树+离散化。
但是并不是裸的叠加。
从头开始思考,展现一下思路过程:首先要维护三种操作,区间置零,区间置1,区间反转,并输出从左到右第一个0的位置。
区间操作必然考虑线段树,但是这个值域维护令人望而却步,但是这么大的值域需要离散化已经是套路的东西了。所以直接把其排序去重映射到一个有限值域中,在这个有限值域建线段树,最后返回的值再映射回去就好了。
然后思考如何维护这几种操作。区间赋值可以打lazy标记,区间反转同样可以打反转标记。打多重lazy标记的时候一定要考虑好标记间的相互影响,换言之,就是两种标记下传的先后问题。那么我们考虑:
区间赋值,之前的反转标记就会失效。区间反转,之前的赋值标记就会取反。它们之间没有先后的顺序,只是每次打新标记的时候直接重新赋值另一种标记就好。
至于求MEX值,很多大佬用了线段树+二分。但是没有必要,只需要先查左子树后查右子树,就可以找到最左的节点。有人说这是贪心,勉强算吧,小贪心?
之后是本题坑点:离散化要把r+1也离散进去。为什么呢?着重讲一讲:因为我们本来想维护值域,但是因为值域太宽广了,所以要离散化。但是并不是所有的都可以离散化。仔细思考:这道题,我们把([l,r])进行离散操作,这个([l,r])就有可能连成一起。那么假如我们要查询(l,r)中间的数,就查不到。这个时候就不能离散化。
但是为什么这道题可以用离散化呢?就是因为我们是整个区间赋值、反转,所以,可以保证答案肯定不会出现在这些区间内部,因为我们本来就是按块操作的。
但是(r+1)不一样,显然,(r+1)是有可能被算成答案的。所以(r+1)也要离散进去。
就大功告成了。
代码:
#include<cstdio>
#include<algorithm>
#define int long long
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read()
{
int x=0,f=1;
char ch=nc();
while(ch<48||ch>57)
if(ch=='-')
f=-1,ch=nc();
while(ch>=48&&ch<=57)
x=x*10+ch-48,ch=nc();
return x*f;
}
const int maxn=1e5+5;
int n;
int opt[maxn],l[maxn],r[maxn];
int a[maxn<<2],cnt;
int sum[maxn<<4],lazy[maxn<<4],rev[maxn<<4];
//sum求和,lazy赋值,rev反转
void pushup(int pos)
{
sum[pos]=sum[lson]+sum[rson];
}
void build(int pos,int l,int r)
{
int mid=(l+r)>>1;
lazy[pos]=-1;
if(l==r)
return;
build(lson,l,mid);
build(rson,mid+1,r);
}
void mark(int pos,int l,int r,int k)
{
if(k==1)
{
sum[pos]=(r-l+1);
lazy[pos]=1;
rev[pos]=0;
}
else if(k==2)
{
lazy[pos]=0;
sum[pos]=0;
rev[pos]=0;
}
else
{
if(lazy[pos]!=-1)
lazy[pos]^=1;
else
rev[pos]^=1;
sum[pos]=(r-l+1)-sum[pos];
}
}
void pushdown(int pos,int l,int r)
{
int mid=(l+r)>>1;
if(lazy[pos]==1)
{
mark(lson,l,mid,1);
mark(rson,mid+1,r,1);
lazy[pos]=-1;
}
else if(lazy[pos]==0)
{
mark(lson,l,mid,2);
mark(rson,mid+1,r,2);
lazy[pos]=-1;
}
if(rev[pos])
{
mark(lson,l,mid,3);
mark(rson,mid+1,r,3);
rev[pos]=0;
}
}
void update(int pos,int l,int r,int x,int y,int k)
{
int mid=(l+r)>>1;
if(l==r)
{
if(k==1)
sum[pos]=1;
else if(k==2)
sum[pos]=0;
else
sum[pos]^=1;
return;
}
if(x<=l && r<=y)
{
mark(pos,l,r,k);
return;
}
pushdown(pos,l,r);
if(x<=mid)
update(lson,l,mid,x,y,k);
if(y>mid)
update(rson,mid+1,r,x,y,k);
pushup(pos);
}
int query(int pos,int l,int r)
{
int mid=(l+r)>>1;
if(l==r)
return l;
pushdown(pos,l,r);
if(sum[lson]<(mid-l+1))
return query(lson,l,mid);
else
return query(rson,mid+1,r);
}
signed main()
{
n=read();
a[++cnt]=1;
for(int i=1;i<=n;i++)
{
opt[i]=read();l[i]=read();r[i]=read();
a[++cnt]=l[i];a[++cnt]=r[i];a[++cnt]=r[i]+1;
}
sort(a+1,a+cnt+1);
cnt=unique(a+1,a+cnt+1)-(a+1);
build(1,1,cnt);
for(int i=1;i<=n;i++)
{
l[i]=lower_bound(a+1,a+cnt+1,l[i])-a;
r[i]=lower_bound(a+1,a+cnt+1,r[i])-a;
update(1,1,cnt,l[i],r[i],opt[i]);
printf("%lld
",a[query(1,1,cnt)]);
}
return 0;
}