zoukankan      html  css  js  c++  java
  • CF796F Sequence Recovery

    题意

    要一个(n)个数的数列(A)。对数列(A)进行(m)次操作:

    • (1space l space r space x) 告诉你当前数列区间([l,r])中的最大值为(x)
    • (2space k space d) 修改数列使得(a[k]=d)

    你需要构造一个最初的符合条件的数列(A),并输出任意一种所有值或起来最大的的数列。

    如果没有符合条件的数列,输出(NO)

    (n,m le 3 imes 10^5,1 le x,d le 10^9),保证(x)两两不同
    传送门

    思路

    可以想到,每个数都会有一个上限,也就是覆盖到它的一操作中(x)的最小值。建一棵线段树来维护上限。但一旦经历过操作二之后,后面的一切限制都是无用的,也就是要知道操作二前的上限,趁这个位置经历第一次操作二前,记录下上限。最后再扫一遍,求出没有操作二的位置的上限。

    但是某些操作二可能会造成操作一不合法,需要建另一棵线段树来维护操作二确定的最大值,每次操作一前查询区间最大值,判断合不合法。

    再来考虑操作一带来的影响,如果说当前的(max[l,r]<x),那么说明([l,r])中至少有一个是(x),记录一下。题目中有一个重要条件,也就是(x)各不相同,所以每个被记录过的值只要出现一次就可以了。但如果整个数列的上限都不存在这个值,就说明后面又被更小的限制覆盖了,也是不合法的。

    接下来在不大于上限的情况下求或值最大的序列。

    (case space 1):有大于两个数没有上限,此时因为(a_i leq 10^9),只要让一个数是(2^{29}),另一个是(2^{29}-1),另外的按上限输出即可

    (case space 2): 对于每一个记录过的上限,是一定要出现一次的。贪心的来,一定是让一个先刚好为上限,而如果还有别的同类限制就可以在不超过上限的数中随便选,肯定是让除了最高位外的所有位都是(1)(最高位已经有了,去掉后肯定小于上限了)。如果剩下一个没有上限的数的话,用(ans)记录一下其它所有的或值,然后从高位往低位,保证小于(10^9)情况下贪心选就行了。

    #include <bits/stdc++.h>
    using std::map;
    using std::min;
    using std::max;
    const int N=300005;
    #define res lim
    int t[N<<2],lim[N],n,m,l,r,x,opt,y,ans,t2[N<<2];
    map <int ,int> c,tag;
    void setmax(int k,int L,int R,int l,int r,int x){//维护上限
    	if (L==l && R==r){
    		t[k]=min(t[k],x);
    		return;
    	}
    	int mid=(L+R)>>1;
    	if (r<=mid) setmax(k<<1,L,mid,l,r,x);
    	else if (l>mid) setmax(k<<1|1,mid+1,R,l,r,x);
    	else{
    		setmax(k<<1,L,mid,l,mid,x);
    		setmax(k<<1|1,mid+1,R,mid+1,r,x);
    	}
    }
    int qry(int k,int l,int r,int x){//查单点上限
    	int ret=t[k];
    	while (l<r){
    		int mid=l+r>>1;
    		if (x<=mid) r=mid,k=k<<1;
    		else l=mid+1,k=k<<1|1;
    		ret=min(ret,t[k]);
    	}
    	return ret;
    }
    void modify(int k,int l,int r,int x,int y){//维护区间最大值
    	if (l==r){
    		t2[k]=y;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if (x<=mid) modify(k<<1,l,mid,x,y);
    	else modify(k<<1|1,mid+1,r,x,y);
    	t2[k]=max(t2[k<<1],t2[k<<1|1]);
    }
    int query(int k,int L,int R,int l,int r){//查区间最大值
    	if (L==l && R==r) return t2[k];
    	int mid=(L+R)>>1;
    	if (r<=mid) return query(k<<1,L,mid,l,r);
    	else if (l>mid) return query(k<<1|1,mid+1,R,l,r);
    	return max(query(k<<1,L,mid,l,mid),query(k<<1|1,mid+1,R,mid+1,r));
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=4*n;i++) t[i]=2e9,t2[i]=-1;
    	for (int i=1;i<=n;i++) lim[i]=2e9+1;
    	while (m--){
    		scanf("%d",&opt);
    		if (opt==1){
    			scanf("%d%d%d",&l,&r,&x);
    			int now=query(1,1,n,l,r);
    			if (now>x){
    				puts("NO");
    				return 0;
    			}
    			if (now<x) tag[x]=1;
    			setmax(1,1,n,l,r,x);
    		}else{
    			scanf("%d%d",&x,&y);
    			if (lim[x]==2e9+1) lim[x]=qry(1,1,n,x);
    			modify(1,1,n,x,y);
    		}
    	}
    	for (int i=1;i<=n;i++)
    		if (lim[i]==2e9+1) lim[i]=qry(1,1,n,i);
    	for (int i=1;i<=n;i++) c[lim[i]]++;
    	for (auto i:tag) 
    	    if (!c[i.first]){
    	    	    puts("NO");
    		    return 0;
    	    } 
    	puts("YES");
    	if (c[2e9]>=2){//case 1
    		for (int i=1;i<=n;i++) 
    			if (lim[i]==2e9){
    				lim[i]=(1<<29)-1;
    				break;
    			}
                    for (int i=1;i<=n;i++){
            	        if (lim[i]==2e9) lim[i]=1e9;
    		        printf("%d ",lim[i]); 
    		}
                    return 0;
    	}
            //case 2
    	for (int i=1;i<=n;i++){
    		if (lim[i]==2e9 || lim[i]==0) continue;
    		c[lim[i]]--;
    		if (c[lim[i]]){
    			int t=1;
    			while (t<=lim[i]) t=t<<1|1;
    			lim[i]=t>>1;
    		}
    		ans|=lim[i];
    	}
    	int tt=0;
            for (int w=29;w>=0;w--){//贪心无限制的数
                if (ans&(1<<w)) continue;
                if(tt+(1<<w)<=1e9) tt+=1<<w;
            }
    	for (int i=1;i<=n;i++){
                if (lim[i]==2e9) lim[i]=tt;
    	    printf("%d ",lim[i]); 
            }
    }
    

    后记

    细节有点多,一不留神就挂了

  • 相关阅读:
    centos7安装kafka
    Qt——透明无边框Widget的bug
    Qt——浅谈样式表
    Qt——QLineEdit使用总结
    Qt——信号槽连接:基于字符串与基于函数的连接之间的不同
    Qt——树的搜索实现源码
    Qt——树结点的搜索
    Qt——鼠标拖动调整窗口大小
    Qt——右键菜单
    Qt——正则表达式
  • 原文地址:https://www.cnblogs.com/flyfeather6/p/12740182.html
Copyright © 2011-2022 走看看