题意
要一个(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]);
}
}
后记
细节有点多,一不留神就挂了