Codeforces Round #495 (Div. 2) F. Sonya and Bitwise OR 题解

203 阅读3分钟

思路

对于区间[l,r][l,r],可以用分治来解决出单次询问的答案。具体来说,有三种区间满足题意:

  • 完全在左半边
  • 完全在右半边
  • 横跨左右两边 可以用尺取法算出第三种答案(因为按位或不会让答案减小,因此每次向右移动左端点,右端点是不减的),然后直接把左右区间的答案维护进去即可。这样复杂度是O(NlogN)O(N\log N)的,考虑如何优化复杂度。

尺取时定住左端点,不断后移右端点,此时我们已经提前维护了左边区间的后缀按位或的和、右边区间的前缀按位或的和。单独考虑这些和,发现最多只有logamax\log a_{max}个不同的数字,因为按位或不会让二进制中的11减少,所以只要有不同,就必然是某些位多了11,并且相同的和的位置是连续的。于是对于每个区间,维护它的前缀、后缀和分别在哪些位置发生了变化,尺取时仅需要在这logamax\log a_{max}个位置上进行就好了,直接把原先复杂度中的NN优化掉,变成logNlogamax\log N\cdot \log a_{max}

因为还有修改操作,就用线段树维护一下。

代码

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