考虑只有一次询问时怎么做。 分治。每次考虑
L
L
L位于左半边,
R
R
R位于右半边的情况(也就是“跨过中点”的答案)。再分别递归左、右两边。计算跨过中点的答案时,可以先求出【左半边的
or
\operatorname{or}
or值后缀和】和【右半边的
or
\operatorname{or}
or值前缀和】。然后用two pointers求出满足条件的
(
L
,
R
)
(L,R)
(L,R)数量,
例如,可以枚举
R
R
R,则左半边的
L
L
L可选范围单调增加,也就是从最左边不断向右移。这样,不算递归,每次做two pointers的复杂度都是
O
(
len
)
O(\text{len})
O(len)的,一次询问总复杂度就是
O
(
len
log
len
)
O(\text{len}\log \text{len})
O(lenloglen)。
分治时的左、右两边,天然就是线段树的左、右节点,这有很多相似之处。但是我们面临的问题是:如果向分治时一样,维护出每个区间的前缀、后缀
or
\operatorname{or}
or和,则一次修改、查询的复杂度,都高达
O
(
n
log
n
)
O(n\log n)
O(nlogn),无法承受。
这里还要一个重要的性质就是前缀
or
\text{or}
or和最多有
log
\text{log}
log个不同的结果!!就是有
log
\text{log}
log个段,每个段里面的值都说一样的,就是共同的位1是一样的,那么合并的时候复杂度就降到
logn
\text{logn}
logn的复杂度了!!!那么总的复杂度就是
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)
AC code
我们开一个线段树支持区间查询,单点修改
线段树里面有个两个数组分别是前缀或pre和后缀或suf 里面还要记录每个块长度
还要一个统计区间的答案cnt
合并的时候直接合并就好了,每次暴力往外区间延申,是可以直接合并的。例如:假设你要统计前缀
or
\text{or}
or那你就要从左儿子的
pre
\text{pre}
pre去合并到右儿子的
pre
\text{pre}
pre,为啥可以直接合并呢?因为对于每个块最左的端的数是包含了这个块内所有位置的1,所以即使你跨边界最先
or
\text{or}
or的还是最左端的数,一旦
or
\text{or}
or这个数,后面的
or
\text{or}
or结果都不会变了
#include<bits/stdc++.h>#definemid((l + r)>>1)#defineLsonrt <<1, l , mid#defineRsonrt <<1|1, mid +1, r#definems(a,al)memset(a,al,sizeof(a))#definelog2(a)log(a)/log(2)#definelowbit(x)((-x)& x)#defineIOSstd::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)#defineINF0x3f3f3f3f#defineLLF0x3f3f3f3f3f3f3f3f#defineffirst#definessecond#defineendl'\n'usingnamespace std;constint N =2e6+10, mod =1e9+9;constint maxn =500010;constlongdouble eps =1e-5;constint EPS =500*500;typedeflonglong ll;typedefunsignedlonglong ull;typedef pair<int,int> PII;typedef pair<ll,ll> PLL;typedef pair<double,double> PDD;template<typenameT>voidread(T &x){
x =0;char ch =getchar();ll f =1;while(!isdigit(ch)){if(ch =='-')f*=-1;ch=getchar();}while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;}template<typenameT,typename... Args>voidread(T &first, Args&... args){read(first);read(args...);}structnode{
ll cnt;
vector<PII> pre, suf;}tr[maxn <<2];int n, m, x;int arr[maxn];inline node merge(node a, node b){if(!a.pre.size())return b;if(!b.pre.size())return a;
node res;
res.cnt = a.cnt + b.cnt;
ll tot =0;for(auto ita : a.suf)for(auto itb : b.pre){if((ita.first|itb.first)>= x)
tot +=1ll* ita.second * itb.second;}
res.cnt += tot;
res.pre = a.pre;
ll num = a.pre.back().first;for(auto itb : b.pre){if((itb.first|num)==num) res.pre.back().second += itb.second;else{
res.pre.push_back({itb.first|num,itb.second});
num = num | itb.first;}}
res.suf = b.suf;
ll num1 = b.suf.back().first;for(auto ita : a.suf){if((ita.first|num1)==num1) res.suf.back().second += ita.second;else{
res.suf.push_back({ita.first|num1,ita.second});
num1 = num1 | ita.first;}}return res;}inlinevoidpushup(int rt){
tr[rt]=merge(tr[rt<<1],tr[rt<<1|1]);}inlinevoidbuild(int rt,int l,int r){if(l == r){
tr[rt].pre.push_back({arr[l],1});
tr[rt].suf.push_back({arr[l],1});
tr[rt].cnt =(arr[l]>= x);return;}build(Lson);build(Rson);pushup(rt);}
node query(int rt,int l,int r,int L,int R){if(L <= l && r <= R)return tr[rt];if(mid < L)returnquery(Rson,L,R);elseif(mid >= R)returnquery(Lson,L,R);elsereturnmerge(query(Lson,L,R),query(Rson,L,R));}voidupdate(int rt,int l,int r,int pos,int val){if(l == r){
tr[rt].pre.back().first = val;
tr[rt].suf.back().first = val;
tr[rt].cnt =(val >= x);return;}if(pos <= mid)update(Lson,pos,val);elseupdate(Rson,pos,val);pushup(rt);}intmain(){
IOS;
cin >> n >> m >> x;for(int i =1; i <= n;++ i) cin >> arr[i];build(1,1,n);while(m--){int op;
cin >> op;if(op ==1){int x, y;
cin >> x >> y;update(1,1,n,x,y);}else{int l, r;
cin >> l >> r;
cout <<query(1,1,n,l,r).cnt << endl;}}}