1004F.Sonya and Bitwise OR(毒瘤线段树+分治思想)

性质:

对一个序列的或前缀和是单调不降的,每次变化至少增加一个二进制位

同时一段区间内发生变化的位置数量<=20(log值域)

那么就可以对线段树上的每个节点维护这样的一组信息:

区间内前缀或发生变化的位置以及它在区间内的前缀或

区间内后缀或发生变化的位置以及它在区间内的后缀或

一个区间内的答案数量,就是它两个子区间的答案加上区间本身的答案。

区间本身的答案计算,就是枚举左区间后缀和和右区间前缀和的每个位置,就是用双指针的方法O(20)求解。因为最多20个点。

现在考虑区间合并。

两个区间的合并,先保留左区间前缀或的变化位置,然后枚举右区间前缀或的每个位置,如果不变就跳过,如果变化就把位置加到父节点的前缀或变化位置里。

同理,保留右区间的后缀或变化位置,然后枚举左区间后缀或的每个位置,如果不变就跳过,如果变化就把位置加到父节点的后缀或变化位置里。

每个区间的答案就是合并后的新答案加上它的两个子区间的答案。

现在考虑区间查询。

查询一个区间,返回一个结构体,包含前缀、后缀、答案。

用刚刚的方法合并两个子查询,并返回上一层函数即可。

现在考虑单点修改。

修改一个点,会影响它的所有父节点,对所有父节点的前后缀变化点暴力修改就完事了。

时间复杂度O(20logn)。

总的时间复杂度O(20nlogn)。

感觉这题是我目前遇到的线段树区间维护天花板了。妥妥的银牌题往上,无论是代码难度还是思维难度。

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*f;
}
typedef long long ll;
const int maxn=2e5+100;
int n,m,a[maxn],X; 
struct node {
	ll ans;
	vector<pair<int,int> > L,R;
	void clear () {
		ans=0;
		L.clear();
		R.clear();
	}
	node () {
		clear();
	}
}segTree[maxn<<2];
node operator + (node x,node y) {
	node ret;
	ret.L=x.L;
	for (int i=0,u=ret.L.back().second;i<y.L.size();i++) {
		pair<int,int> tt=y.L[i];
		if ((tt.second|u)!=u) {
			u|=tt.second;
			ret.L.push_back(make_pair(tt.first,u)); 
		}
	}
	ret.R=y.R;
	reverse(ret.R.begin(),ret.R.end());
	for (int i=x.R.size()-1,u=ret.R.back().second;i>=0;i--) {
		pair<int,int> tt=x.R[i];
		if ((tt.second|u)!=u) {
			u|=tt.second;
			ret.R.push_back(make_pair(tt.first,u));
		}
	}
	reverse(ret.R.begin(),ret.R.end());
	ret.ans=x.ans+y.ans;
	for (int i=0,u=0;i<y.L.size();i++) {
		while (u<x.R.size()&&(x.R[u].second|y.L[i].second)>=X) u++;
		int ll=(!u?0:x.R[u-1].first-x.L[0].first+1);
		int rr=(i+1==y.L.size()?y.R.back().first-y.L[i].first+1:y.L[i+1].first-y.L[i].first);
		ret.ans+=1ll*ll*rr;
	}
	return ret;
}
void build (int i,int l,int r) {
	segTree[i].clear();
	if (l==r) {
		segTree[i].L.push_back(make_pair(l,a[l]));
		segTree[i].R.push_back(make_pair(l,a[l]));
		segTree[i].ans=(a[l]>=X);
		return;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	segTree[i]=segTree[i<<1]+segTree[i<<1|1];
}
void up (int i,int l,int r,int p,int v) {
	if (l==r) {
		segTree[i].clear();
		segTree[i].L.push_back(make_pair(p,v));
		segTree[i].R.push_back(make_pair(p,v));
		segTree[i].ans=(v>=X);
		return;
	}
	int mid=(l+r)>>1;
	if (p<=mid) up(i<<1,l,mid,p,v);
	if (p>mid) up(i<<1|1,mid+1,r,p,v);
	segTree[i]=segTree[i<<1]+segTree[i<<1|1]; 
}
node query (int i,int l,int r,int L,int R) {
	if (l>=L&&r<=R) return segTree[i];
	int mid=(l+r)>>1;
	if (R<=mid) return query(i<<1,l,mid,L,R);
	else if (L>mid) return query(i<<1|1,mid+1,r,L,R);
	else return query(i<<1,l,mid,L,R)+query(i<<1|1,mid+1,r,L,R);
}
main (){
	n=read();
	m=read();
	X=read();
	for (int i=1;i<=n;i++) a[i]=read();
	build(1,1,n);
	for (int i=1;i<=m;i++) {
		int op=read();
		int x=read();
		int y=read();
		if (op==1)
			up(1,1,n,x,y);
		else
			printf("%lld\n",query(1,1,n,x,y).ans);
	}
}
posted @ 2021-05-27 01:47  zlc0405  阅读(57)  评论(0编辑  收藏  举报