• CF817F MEX Queries


    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

      种操作:

      1. 把 [l,r][l,r] 中在集合中没有出现过的数添加到集合中。
      2. 把 [l,r][l,r] 中在集合中出现过的数从集合中删掉。
      3. 把 [l,r][l,r] 中在集合中没有出现过的数添加到集合中,并把 [l,r][l,r] 中在集合中出现过的数从集合中删掉。
    • 每次操作后输出集合的 operatorname{MEX}MEX —— 在集合中没有出现过的最小正整数。

    • 1le nle 10^51≤n≤105,1le lle rle 10^{18}1≤lr≤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;
    }
    
  • 相关阅读:
    ASP.NET的最新安全漏洞Important: ASP.NET Security Vulnerability
    Sql常用日期格式
    倒计时 服务器时间 .NET js javascript
    “备份集中的数据库备份与现有的数据库不同”解决方法
    2010年最佳jQuery插件
    jQuery1.4与json格式兼容问题
    .NET结束外部进程 C#结束外部进程
    十步优化SQL Server中的数据访问
    SQL游标的使用与语法
    SQL2005、SQL2008如何压缩日志文件(log) 如何清除日志
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13965392.html
Copyright © 2020-2023  润新知