P11071 「QMSOI R1」 Distorted Fate

dcytrl / 2024-09-19 / 原文

介绍一种好想、在线、空间小、跑的还挺快的做法(?)


先暂时不考虑修改,只考虑怎么快速求解询问。

询问相当于区间内前缀按位或的和。根据按位或的性质,当区间内某个值在某一位下是 \(1\),那么所有包含这个值的前缀的按位或结果在该位下都为 \(1\)

考虑拆位,单独考虑每一位对答案的贡献,设 \(w\) 是当前枚举的二进制位,\([l,r]\) 内从左往右第一个在该位下为 \(1\) 的数的下标为 \(p\),则这一位的贡献为 \(2^w\times(r-p+1)\)。我们只需要用一个数据结构快速查询区间内第一个某一位为 \(p\),考虑到我们还有修改,我们选择用线段树维护,开 \(O(\log V)\) 棵线段树,每个节点维护第一个 0 和第一个 1 的出现位置。时间复杂度 \(O(n\log n\log V)\),空间复杂度 \(O(n\log V)\),精细实现应该可以过题。


感觉开 \(O(\log V)\) 棵线段树有点太浪费了,能不能只开一棵呢?

可以!

线段树维护区间 OR 值,先考虑查询,将询问区间划分成 \(O(\log n)\) 段,因为我们只需要知道每一位第一次出现的位置在哪里,所以处理某个区间时,设前面的区间 OR 值是 \(v\),对于这个区间,我们只需要考虑 \(v\)\(0\) 的那些二进制位即可。如果没有,直接退出。

将询问区间划分好了之后,每个区间内部的处理和上面是一样的:当递归右子树时,求出左子树的 OR 值,仍设其为 \(v\),那么在右子树中只需要考虑 \(v\)\(0\) 的二进制位即可,如果没有就退出。

分析时间复杂度。由于只有对答案有贡献的那些链会一直往下递归到叶子,而这些从根走向叶子的链至多只有 \(O(\log V)\) 条,每条这样的链的深度为 \(O(\log n)\),故时间复杂度 \(O(n\log n\log V)\)


接着考虑修改。众所周知,对区间异或 \(x\) 后最终的区间 OR 值不等于最初的 OR 值异或 \(x\)。不难发现,对于某一位,当区间内的所有数在这一位上不全为 \(1\),这个方法会出错。考虑再维护一个区间 AND 值,那么出问题的数位在区间 AND 值中为 \(0\)。将异或 \(x\) 后的区间 OR 值上(不能异或)区间 AND 值在模 \(2^{30}\) 次方意义下按位取反后的值,得到的就是正确的区间 OR 值。

区间 AND 值同理,异或 \(x\) 后发现出问题的数位在区间 OR 值中为 \(1\),只需要再与上区间 OR 值在模 \(x\) 意义下按位取反后的值(\(x\)\(0\) 的数位要赋成 \(1\))即可。具体可以看代码。


至此这道题就做完了,时间复杂度 \(O(n\log n\log V)\),由于我们只需要维护 tag 以及区间 OR 和 AND 的值,所以空间复杂度为 \(O(n)\)。但注意到这 \(\log V\) 条从根到叶子的链是会有很大一部分重复的,所以这个 2log 严重跑不满。实际运行效率很高,最大点跑了 130+ms。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<ctime>
#include<random>
#include<cassert>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0)
#define OTIE cout.tie(0)
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define un using namespace
#define popc __builtin_popcountll
#define all(x) x.begin(),x.end()
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) (x&-x)
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
#define mem(x,y) memset(x,y,sizeof x)
//#define double long double
//#define int unsigned int
//#define int __int128
using namespace std;
typedef long long i64;
typedef unsigned long long u64;
using pii=pair<int,int>;
bool greating(int x,int y){return x>y;}
bool greatingll(long long x,long long y){return x>y;}
inline int rd(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}return x*f;
}
template<typename T>
inline void write(T x,char ch='\0'){
	if(x<0){x=-x;putchar('-');}
	int y=0;char z[40];
	while(x||!y){z[y++]=x%10+48;x/=10;}
	while(y--)putchar(z[y]);if(ch!='\0')putchar(ch);
}
bool Mbg;
const int maxn=2e5+5,maxm=4e5+5,inf=0x3f3f3f3f,mod=(1<<30)-1;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,Q,a[maxn];
int d[2][maxn<<2];
int tag[maxn<<2];
#define mid ((l+r)>>1)
inline void pu(int p){
	d[0][p]=d[0][lson(p)]|d[0][rson(p)];
	d[1][p]=d[1][lson(p)]&d[1][rson(p)];
}
inline void pt(int p,int v){
	int _0=d[0][p],_1=d[1][p];
	d[0][p]=(_0^v)|((_1^mod)&v);
	d[1][p]=(_1^v)&((_0^mod)|(mod^v));
	tag[p]^=v;
}
inline void pd(int p){
	if(tag[p]){
		pt(lson(p),tag[p]),pt(rson(p),tag[p]);
		tag[p]=0;
	}
}
void bd(int l=1,int r=n,int p=1){
	if(l==r)return d[0][p]=d[1][p]=a[l],void();
	bd(l,mid,lson(p)),bd(mid+1,r,rson(p)),pu(p);
}
inline void upd(int ll,int rr,int v,int l=1,int r=n,int p=1){
	if(ll<=l&&r<=rr)return pt(p,v);
	pd(p);
	if(ll<=mid)upd(ll,rr,v,l,mid,lson(p));
	if(rr>mid)upd(ll,rr,v,mid+1,r,rson(p));
	pu(p);
}
inline pii operator+(pii x,pii y){
	return mp(x.fi|y.fi,(x.se+y.se)&mod);
}
inline pii qry(int ed,int v,int l,int r,int p){
	if((v|d[0][p])==v)return mp(0,0);
	if(l==r)return mp(d[0][p],(1ll*(ed-l+1)*(d[0][p]-(d[0][p]&v)))&mod);
	pd(p);
	pii res=qry(ed,v,l,mid,lson(p));
	return res+qry(ed,v|res.fi,mid+1,r,rson(p));
}
inline pii _qry(int ll,int rr,int v=0,int l=1,int r=n,int p=1){
	if((v|d[0][p])==v)return mp(0,0);
	if(ll<=l&&r<=rr)return qry(rr,v,l,r,p);
	pd(p);
	if(rr<=mid)return _qry(ll,rr,v,l,mid,lson(p));
	if(ll>mid)return _qry(ll,rr,v,mid+1,r,rson(p));
	pii res=_qry(ll,rr,v,l,mid,lson(p));
	return res+_qry(ll,rr,v|res.fi,mid+1,r,rson(p));
}
#undef mid
inline void solve_the_problem(){
	n=rd(),Q=rd();
	rep(i,1,n)a[i]=rd();
	bd();
	while(Q--){
		int op=rd(),l=rd(),r=rd(),x;
		if(op==1){
			x=rd();
			upd(l,r,x);
		}else{
			pii res=_qry(l,r);
			write(res.se,10);
		}
	}
}
bool Med;
signed main(){
//	freopen(".in","r",stdin);freopen(".out","w",stdout);
//	fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
	int _=1;
	while(_--)solve_the_problem();
}
/*

*/