mybatis 動態sql!!!
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
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
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
- 把 [l,r][l,r] 中在集合中出現過的數從集合中刪掉。
- 把 [l,r][l,r] 中在集合中沒有出現過的數新增到集合中,並把 [l,r][l,r] 中在集合中出現過的數從集合中刪掉。
- 把 [l,r][l
-
每次操作後輸出集合的 \operatorname{MEX}MEX —— 在集合中沒有出現過的最小正整數。
-
1\le n\le 10^51≤n≤105,1\le l\le r\le 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\n",a[query(1,1,cnt)]);
}
return 0;
}