与或

slojP2604. 与或

题目大意

有一个长度为\(n\)的数列\(A\),有\(Q\)个操作:

1 L R X对于\(L≤i≤R\),把\(A_i\)变成\(A_i\wedge X\)

2 L R X对于\(L≤i≤R\),把\(A_i\)变成\(A_i\vee X\)

3 L R 询问\(A_L,A_{L+1},..A_R\)中的最大值。

\(\wedge\)表示按位与,\(\vee\)表示按位或。

数据范围

对于\(30\%\)数据,\(N,Q<=2000\)

另有\(30\%\)数据,第三类操作\(L==R\)

对于\(100\%\)数据,\(N,Q<=200000,Ai∈[ 0 , 2 ^ {20} )\),数据保证随机


解析

首先我们需要明确或操作和与操作分别的作用是什么,

按位或操作,对于两个进行按位或操作的二进制数的同一位,只要其中一个为1,那么答案的这一位就为1。

按位与操作,对于两个进行按位与操作的二进制数的同一位,只要其中一个为0,那么答案的这一位就为0。

敲黑板,这是重点

如果使用按位与来进行修改,那么如果\(x\)的第\(i\)位是0,那么答案的第\(i\)位也被强制成了0,

同理,如果使用按位或来进行修改,那么如果\(x\)的第\(i\)位是1,那么答案的第\(i\)位也被强制成了1,

每次操作之后,都有一段区间中的数的某些二进制位被修改成了相同的,就是上面所说的两点推导得出

而整个序列中相同的二进制位数随着修改次数的增多显然是单调不降的,举个例子,假设说所有的这一位本来是不一样的,但是或操作中该位为1,那么这些数的这位也会都变为1,如果这时候再接上别的操作,例如与操作的数这位上是0,那么所有的都会也变为0,所以它具有单调性,不降则是因为有可能只在本就已经相同的几位上做出了变化

这样的话,由于每个数最多有20位自己看范围,所以即使我们使用带有暴力思想的线段树也是可以\(A\)掉这题的。明显是一种假做法,但它就是能过

由于使整个区间二进制位相同的修改次数是单调不降的,我们不妨直接在修改当前线段树节点的判断条件上动手脚,我们知道原本修改当前线段树节点的判断条件是\((ql≤T[p].l \quad \&\& \quad T[p].r≤qr)\),

我们不妨再增加一个限制条件\(T[p].minn==T[p].maxn\)

那么真做法是什么呢?

第一种:写两个\(update\)函数打区间懒标记分别对操作\(1,2\)进行储存修改

第二种:我们利用位运算将按位与的修改转化成为按位或的修改,代码量较少,但是很容易被弄昏

作者太懒了只写了第二种

上代码

#include<bits/stdc++.h>
#define inf (1<<20)-1
using namespace std;
struct node{
	int l,r,sm,mx;
}t[800010];
int n,q,a[200010];
void pushnow(int p,int sm,int mx){
	t[p].sm|=sm;
	t[p].mx = (t[p].mx&(~sm)|(mx&sm));
}
void pushdown(int p){
	pushnow(p*2,t[p].sm,t[p].mx);
	pushnow(p*2+1,t[p].sm,t[p].mx);
}
void pushup(int p){
	t[p].sm = (~(t[p*2].mx^t[p*2+1].mx))&(t[p*2].sm&t[p*2+1].sm);
	t[p].mx = max(t[p*2].mx,t[p*2+1].mx);
}
void build(int p,int l,int r){
	t[p].l = l,t[p].r = r;
	if(t[p].l==t[p].r){
		t[p].sm = inf;
		t[p].mx = a[l];
		return ;
	}
	int mid = (t[p].l+t[p].r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	pushup(p);
}
void update(int p,int ql,int qr,int sm,int v){
	if(qr<t[p].l||t[p].r<ql)return;
	if(ql<=t[p].l&&t[p].r<=qr){
		if(t[p].l==t[p].r||((sm&t[p].sm)==sm)){
			t[p].mx = (t[p].mx&(~sm))|(v&sm);
			return;
		}
	}
	pushdown(p);
	update(p*2,ql,qr,sm,v);
	update(p*2+1,ql,qr,sm,v);
	pushup(p);
}
int query(int p,int ql,int qr){
	if(qr<t[p].l||t[p].r<ql) return 0;
	if(ql<=t[p].l&&t[p].r<=qr) return t[p].mx;
	pushdown(p);
	return max(query(p*2,ql,qr),query(p*2+1,ql,qr));
}
int main(){
	scanf("%d%d",&n,&q);
	for(int i = 1;i<=n;i++)
		scanf("%d",&a[i]);
	build(1,1,n);
	for(int i = 1;i<=q;i++){
		int opt,l,r,x;
		scanf("%d%d%d",&opt,&l,&r);
		if(opt==1){
			scanf("%d",&x);
			update(1,l,r,inf^x,0);
		}else if(opt==2){
			scanf("%d",&x);
			update(1,l,r,x,x);
		}else printf("%d\n",query(1,l,r));
	}
	return 0;
}
posted @ 2023-02-07 16:22  cztq  阅读(88)  评论(0)    收藏  举报