CF796F Sequence Recovery

题意

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

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

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

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

\(n,m \le 3\times 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]); 
        }
}

后记

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

posted @ 2020-04-20 20:23  flyfeather  阅读(266)  评论(0)    收藏  举报