思路
对于区间,可以用分治来解决出单次询问的答案。具体来说,有三种区间满足题意:
- 完全在左半边
- 完全在右半边
- 横跨左右两边 可以用尺取法算出第三种答案(因为按位或不会让答案减小,因此每次向右移动左端点,右端点是不减的),然后直接把左右区间的答案维护进去即可。这样复杂度是的,考虑如何优化复杂度。
尺取时定住左端点,不断后移右端点,此时我们已经提前维护了左边区间的后缀按位或的和、右边区间的前缀按位或的和。单独考虑这些和,发现最多只有个不同的数字,因为按位或不会让二进制中的减少,所以只要有不同,就必然是某些位多了,并且相同的和的位置是连续的。于是对于每个区间,维护它的前缀、后缀和分别在哪些位置发生了变化,尺取时仅需要在这个位置上进行就好了,直接把原先复杂度中的优化掉,变成。
因为还有修改操作,就用线段树维护一下。
代码
#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
#define mp make_pair
#define eb emplace_back
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
int __=0;
_=0;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
__=1;
ch=getchar();
}
while(isdigit(ch))
{
_=_*10+ch-48;
ch=getchar();
}
_=__?-_:_;
read(oth...);
}
template <typename T>
void Out(T _)
{
if(_<0)
{
putchar('-');
_=-_;
}
if(_>=10)
Out(_/10);
putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
Out(_);
putchar('\n');
OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
Out(_);
putchar(' ');
op(oth...);
}
/*#################################*/
const ll N=1E5+10;
ll n,m,x;
ll a[N],ans[4*N];
vector<pii> pre[4*N],nxt[4*N];
ll calc(ll p,ll al,ll ar)
{
ll R=0,ret=0;
for(int L=nxt[lc].size()-1;L>=0;--L)
{
if(al>nxt[lc][L].first)
continue;
ll lsum=nxt[lc][L].second;
while(R<pre[rc].size() && pre[rc][R].first<=ar && (lsum | pre[rc][R].second)<x)
++R;
if(R==pre[rc].size() || pre[rc][R].first>ar)
break;
ll PRE;
if(L==nxt[lc].size()-1)
PRE=al;
else
PRE=max(al,nxt[lc][L+1].first+1);
ret+=(ar-pre[rc][R].first+1)*(nxt[lc][L].first-PRE+1);
}
return ret;
}
void update(ll p,ll l,ll r)
{
pre[p]=pre[lc];
ll sum=pre[p].back().second;
for(auto it:pre[rc])
if((sum | it.second)!=sum)
pre[p].eb(it.first,sum |= it.second);
nxt[p]=nxt[rc];
sum=nxt[p].back().second;
for(auto it:nxt[lc])
if((sum | it.second)!=sum)
nxt[p].eb(it.first,sum |= it.second);
ans[p]=ans[lc]+ans[rc]+calc(p,l,r);
}
void build(ll p,ll l,ll r)
{
if(l==r)
{
pre[p].eb(l,a[l]);
nxt[p].eb(l,a[l]);
if(a[l]>=x)
ans[p]=1;
return;
}
ll mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
update(p,l,r);
}
void modify(ll p,ll l,ll r,ll pos,ll val)
{
if(l==r)
{
pre[p].clear();
nxt[p].clear();
ans[p]=0;
pre[p].eb(l,val);
nxt[p].eb(l,val);
if(val>=x)
ans[p]=1;
return;
}
ll mid=(l+r)>>1;
if(pos<=mid)
modify(lc,l,mid,pos,val);
else
modify(rc,mid+1,r,pos,val);
update(p,l,r);
}
ll query(ll p,ll l,ll r,ll al,ll ar)
{
if(al<=l && ar>=r)
return ans[p];
ll mid=(l+r)>>1;
if(al>mid)
return query(rc,mid+1,r,al,ar);
if(ar<=mid)
return query(lc,l,mid,al,ar);
return query(lc,l,mid,al,ar)+query(rc,mid+1,r,al,ar)+calc(p,max(al,l),min(ar,r));
}
void print()
{
for(auto it:nxt[2])
{
op(it.first,it.second);
en;
}
en;
for(auto it:pre[3])
{
op(it.first,it.second);
en;
}
en;
}
int main()
{
read(n,m,x);
rep(i,1,n)
read(a[i]);
build(1,1,n);
ll ty,l,r;
while(m--)
{
read(ty,l,r);
if(ty==1)
modify(1,1,n,l,r);
else
OP(query(1,1,n,l,r));
}
}