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);
}
}