1. 程式人生 > 實用技巧 >CF1004F Sonya and Bitwise OR 線段樹區間合併

CF1004F Sonya and Bitwise OR 線段樹區間合併

先提出一個顯而易見的性質:
字首或是遞增的 並且只有logn種不同的值 且相同的值是連續的
遞增性顯然 因字首或只會在原來的基礎上加某些位 最多加log個位
所以最多logn種不同的值
再來看這題
首先區間問題 不出意外的話 都是線段樹
我們先考慮不帶修改的怎麼寫
因為或運算的遞增性 顯然我們可以雙指標 在o(n)的時間求出一個答案
然後利用歸併的思想 我們可以nlogn解決 不帶修改的詢問
如果帶修改呢 在上述情況下暴力 修改 單次複雜度就達到了nlogn 不可行
這時候我們得利用一些logn種不同值這個性質
對於每個區間 維護字首or與字尾or 用兩個vector存下logn個不同的塊
然後暴力合併就可以了
單次修改複雜度 log(a)logn
單次詢問複雜度 log(a)logn
參考dls題解

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
#define pa pair<int,int>
#define ls id<<1
#define rs ls|1
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) ((int)x.size())
int n,m,x,a[N];
typedef long long ll;
struct node{
	int l,r;
	ll ans;
	vector<pa>pre,suf;
	node(){}
}T[N<<2];
void ck(int id){
	assert(sz(T[id].pre)&&T[id].l==T[id].pre[0].fi);
	assert(sz(T[id].suf)&&T[id].r==T[id].suf[0].fi);
}
void pushup(int id){
	T[id].ans=T[ls].ans+T[rs].ans;
	ck(ls);ck(rs);
	for(int i = 0,j = sz(T[ls].suf); i < sz(T[rs].pre); i++){
		int nex = (i<sz(T[rs].pre)-1?T[rs].pre[i+1].fi:T[rs].r+1);
		while(j>0&&(T[ls].suf[j-1].se|T[rs].pre[i].se)>=x) j--;
		if(j!=sz(T[ls].suf))
		T[id].ans+=1ll*(nex-T[rs].pre[i].fi)*(T[ls].suf[j].fi-T[ls].l+1);
	}
	
	// pre
	T[id].pre=T[ls].pre;
	for(int i = 0; i < sz(T[rs].pre); i++){
		if(T[id].pre.back().se!=(T[id].pre.back().se|T[rs].pre[i].se))
		T[id].pre.pb(mp(T[rs].pre[i].fi,T[id].pre.back().se|T[rs].pre[i].se));
	}
	
	//suf
	T[id].suf=T[rs].suf;
	for(int i = 0; i < sz(T[ls].suf); i++){
		if(T[id].suf.back().se!=(T[id].suf.back().se|T[ls].suf[i].se))
		T[id].suf.pb(mp(T[ls].suf[i].fi,T[id].suf.back().se|T[ls].suf[i].se));
	}
	
	assert(sz(T[id].pre)<=21);
	assert(sz(T[id].suf)<=21);
}
void build(int id,int l,int r){
	T[id].l=l,T[id].r=r;
	if(l==r){
		T[id].ans=(a[l]>=x);
		T[id].pre.pb(mp(l,a[l]));
		T[id].suf.pb(mp(l,a[l]));
		return;
	}
	int mid = l+r>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	pushup(id);
}
void update(int id,int l,int r,int pos){
	if(l==r){
		T[id].ans=(a[l]>=x);
		T[id].pre[0]=mp(l,a[l]);
		T[id].suf[0]=mp(l,a[l]);
		return;
	}
	int mid = l+r>>1;
	if(pos<=mid) update(ls,l,mid,pos);
	else update(rs,mid+1,r,pos);
	pushup(id);
}
ll query(int id,int L,int R){
	assert(T[id].l<=L); assert(R<=T[id].r);
	if(L==T[id].l&&R==T[id].r) return T[id].ans;
	int mid = T[id].l+T[id].r>>1;
	if(R<=mid) return query(ls,L,R);
	else if(L>mid) return query(rs,L,R);
	else{
		ll ret=query(ls,L,mid)+query(rs,mid+1,R);
		int j=sz(T[ls].suf)-1;
		while(T[ls].suf[j].fi<L) j--;
		for(int i = 0; i < sz(T[rs].pre) && T[rs].pre[i].fi <= R; i++){
			int nex = (i<sz(T[rs].pre)-1?T[rs].pre[i+1].fi:T[rs].r+1);
			nex = min(nex,R+1);
			if((T[ls].suf[j].se|T[rs].pre[i].se)<x) continue;
			while(j>0&&(T[ls].suf[j-1].se|T[rs].pre[i].se)>=x)
			j--;
			ret+=1ll*(nex-T[rs].pre[i].fi)*(T[ls].suf[j].fi-L+1);
		}
		return ret;
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&x);
	for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
	build(1,1,n);
	for(int i = 1; i <= m; i++){
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		if(op==1){
			a[x]=y;
			update(1,1,n,x);
		}else{
			printf("%lld\n",query(1,x,y));
		}
	}
	return 0;
}