mex (离散化+线段树)

Posted RogerDTZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mex (离散化+线段树)相关的知识,希望对你有一定的参考价值。

  Time Limit3000 ms   Memory Limit256 MB

 

Description

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

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

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

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

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

Input

  第一行一个整数n,表示有n个操作

  接下来n行,每行3个整数op,l,r表示一个操作

Output

  共n行,一行一个整数表示答案

 

Sample Input

Sample Output

3
1 3 4
3 1 6
2 1 3
1
3
1

 

HINT

  对于30%的数据$1\\le n\\le 10^3,1\\le l\\le r\\le 10^{18}$

  对于100%的数据$1\\le n\\le 10^5,1\\le l\\le r\\le 10^{18}$

 

 


 

题解

  离散化操作区间:

  首先看到$1\\le l\\le r\\le 10^{18}$的范围,第一反应离散化。

    这题的离散化是非常讲究的,我们不能只把每个区间操作的两端点提取出来离散化(我就是这么干的,其他部分都是对的,结果眼睁睁地没法输出),因为这样无法考虑到区间之间的点,如下图:

  

 

 

  考虑到答案的位置,答案应该只可能出现在某一段操作区间右端点的右边一位,于是我们在离散一个操作区间$l,r$的同时,把$r+1$也离散化掉。操作并不涉及到$r+1$,仅仅是为了输出答案的正确和可行性。

 

  实现修改操作:

    看到熟悉的区间操作,当然要想想线段树啦。

     这里采用两棵线段树的写法实现3种操作,当然也有一棵线段树搞定的写法。

     两棵线段树$A,B$,都先按照离散化的规模建好,以离散化端点编号为索引。$A$记录$0$的信息,$B$记录$1$的信息。

     线段树维护的信息是:$0$或$1$最左出现的位置。

     第1个操作:将$A$的相应区间的信息清空(设置成最大值,因为不存在$0$了),将$B$的相应区间的信息填充(设置成区间的左端点位置),并打上清空或填充标记。日后记得下传。

     第2个操作:与第1个操作完全相反。

       第3个操作:将$A$的相应区间节点和$B$的相应区间节点对调。

 

  输出:

    询问$A$中的最小值位置,输出离散化前的原值即可。可以发现这个点一定是某个区间的$r$再$+1$。

 

  时间复杂度$O(n lg n)$,空间复杂度$O(n)$。


 

  1 #include <cstdio>
  2 #include <algorithm>
  3 #define min(a,b) (a<b?a:b)
  4 using namespace std;
  5 typedef long long ll;
  6 const int N=1e5+10;
  7 ll INF=1000000000000000001;
  8 int n,lshtot,opt[N][3],total;
  9 ll inp[N][3],lis[N*3],minloc,maxloc,orival[N*3];
 10 void lshAndfill(){
 11     sort(lis+1,lis+1+lshtot);
 12     total=unique(lis+1,lis+1+lshtot)-lis-1;
 13     for(int i=1;i<=total;i++) orival[i]=lis[i];
 14     orival[total+1]=INF;
 15     for(int i=1;i<=n;i++){
 16         opt[i][0]=inp[i][0];
 17         opt[i][1]=lower_bound(lis+1,lis+1+total,inp[i][1])-lis;
 18         opt[i][2]=lower_bound(lis+1,lis+1+total,inp[i][2])-lis;
 19     }
 20 }
 21 struct Seg{
 22     int cnt,root[2],sz,ch[N*13][2],mark[N*13];
 23     ll info[N*13];
 24     void build(int Size){
 25         sz=Size;
 26         _build(root[0],1,sz,true);
 27         _build(root[1],1,sz,false);
 28     }
 29     void _build(int &u,int l,int r,bool isfill){
 30         if(!u) u=++cnt;
 31         mark[u]=-1;
 32         if(l==r){
 33             if(isfill) info[u]=l;
 34             else info[u]=total+1;
 35             return;
 36         }
 37         int mid=(l+r)>>1;
 38         _build(ch[u][0],l,mid,isfill);
 39         _build(ch[u][1],mid+1,r,isfill);
 40         pushup(u);
 41     }
 42     inline void pushup(int u){
 43         info[u]=min(info[ch[u][0]],info[ch[u][1]]);
 44     }
 45     inline void pushdown(int u,int l,int r){
 46         int lc=ch[u][0],rc=ch[u][1];
 47         if(mark[u]==-1) return;
 48         mark[lc]=mark[rc]=mark[u];
 49         if(mark[u]==0)
 50             info[lc]=info[rc]=total+1;
 51         else{
 52             info[lc]=l;
 53             info[rc]=(l+r)/2+1;
 54         }
 55         mark[u]=-1;
 56     }
 57     void setSeg(int flag,int l,int r){
 58         _setSeg(root[0^flag],root[1^flag],1,sz,l,r);
 59     }    
 60     void _setSeg(int u1,int u2,int l,int r,int L,int R){
 61         if(L<=l&&r<=R){
 62             info[u1]=total+1;
 63             info[u2]=l;
 64             mark[u1]=0; mark[u2]=1;
 65             return;
 66         }
 67         pushdown(u1,l,r);
 68         pushdown(u2,l,r);
 69         int mid=(l+r)>>1;
 70         if(L<=mid) _setSeg(ch[u1][0],ch[u2][0],l,mid,L,R);
 71         if(mid<R) _setSeg(ch[u1][1],ch[u2][1],mid+1,r,L,R);
 72         pushup(u1);
 73         pushup(u2);
 74     }
 75     void swapSeg(int l,int r){_swapSeg(root[0],root[1],1,sz,l,r);}
 76     void _swapSeg(int &u1,int &u2,int l,int r,int L,int R){
 77         if(L<=l&&r<=R){
 78             swap(u1,u2);
 79             return;
 80         }
 81         pushdown(u1,l,r);
 82         pushdown(u2,l,r);
 83         int mid=(l+r)>>1;
 84         if(L<=mid) _swapSeg(ch[u1][0],ch[u2][0],l,mid,L,R);
 85         if(mid<R) _swapSeg(ch[u1][1],ch[u2][1],mid+1,r,L,R);
 86         pushup(u1);
 87         pushup(u2);
 88     }
 89     inline ll getMin(int x){return info[root[x]];}
 90 }seg;
 91 int main(){
 92     scanf("%d",&n);
 93     minloc=-1; 
 94     for(int i=1;i<=n;i++){
 95         scanf("%lld%lld%lld",&inp[i][0],&inp[i][1],&inp[i][2]);
 96         lis[++lshtot]=inp[i][1];
 97         lis[++lshtot]=inp[i][2];
 98         lis[++lshtot]=inp[i][2]+1;
 99         if(minloc==-1) minloc=min(inp[i][1],inp[i][2]);    
100         else minloc=min(minloc,min(inp[i][1],inp[i][2]));
101     }
102     lshAndfill();
103     seg.build(total);
104     for(int i=1;i<=n;i++){
105         if(minloc>1){
106             printf("1\\n");
107             continue;
108         }
109         if(opt[i][0]<=2)
110             seg.setSeg(opt[i][0]==2,opt[i][1],opt[i][2]);    
111         else
112             seg.swapSeg(opt[i][1],opt[i][2]);
113         printf("%lld\\n",orival[seg.getMin(0)]);
114     }
115     return 0;
116 }
奇妙代码

 

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

BZOJ.3585.mex(线段树)

线段树一些题

Codeforces817 F. MEX Queries(线段树,01序列同时维护翻转laz和覆盖laz)

Codeforces 817F - MEX Queries

CF817F MEX Queries(线段树上二分)

hdu1542线段树(扫描线+离散化)