【主席树】CC_PREFIXOR Prefix XOR

【题目】
CC
给定一个长度为 n n n的序列 a a a,有 q q q个询问 [ l , r ] [l,r] [l,r],回答区间内满足前缀异或和单调不降的区间 [ i , j ] [i,j] [i,j]有多少个。强制在线。
n ≤ 4 × 1 0 5 , a i ≤ 1 0 9 n\leq 4\times 10^5,a_i\leq 10^9 n4×105,ai109

【解题思路】
根据套路,我们首先肯定是求一个前缀异或和,然后要求 s j ⊕ s i − 1 ≥ s j − 1 ⊕ s i − 1 s_j\oplus s_{i-1}\ge s_{j-1}\oplus s_{i-1} sjsi1sj1si1
一个可能可行的思路是我们求出对于每个 i i i,往右最远的上升区间能到哪里,也许可以往下做。
那么我们看看怎么求这个东西,显然我们只需要考虑相邻两位二进制下最高不同的位。于是从右往左枚举 i i i,记录每一位的限制为 0 / 1 0/1 0/1时的位置即可。每次找一个最近的限制位来得到这个点的值。
不妨将上面这个东西记为 f i f_i fi,我们要求的就是:
∑ i = l r ( min ⁡ ( r , f i ) − i + 1 ) \sum_{i=l}^r (\min (r,f_i)-i+1) i=lr(min(r,fi)i+1)
后半部分很简单,于是只需要考虑前半部分。
实际上前半部分也很简单,只需要得到大于 r r r的所有 i ∈ [ l , r ] i\in[l,r] i[l,r]中的 f i f_i fi个数即可。那么对于序列建立主席树,维护 f i f_i fi的区间和及个数即可。

最后复杂度 O ( 30 n + ( n + q ) log ⁡ n ) O(30n+(n+q)\log n) O(30n+(n+q)logn)

【参考代码】

#include
using namespace std;

typedef long long ll;
const int N=4e5+10,M=N*25;

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;

namespace Data_Structure
{
	int rt[N];
	struct Segment
	{
		int sz,ls[M],rs[M],cnt[M];
		ll sum[M];
		void copy(int x,int y){ls[x]=ls[y];rs[x]=rs[y];cnt[x]=cnt[y];sum[x]=sum[y];}
		void update(int &x,int y,int l,int r,int p)
		{
			x=++sz;copy(x,y);cnt[x]++;sum[x]+=p;
			if(l==r) return;
			int mid=(l+r)>>1;
			if(p<=mid) update(ls[x],ls[y],l,mid,p);
			else update(rs[x],rs[y],mid+1,r,p);
		}
		int queryc(int x,int y,int l,int r,int L,int R)
		{
			if(L>R) return 0;
			if(L<=l && r<=R) return cnt[y]-cnt[x];
			int mid=(l+r)>>1;int res=0;
			if(L<=mid) res+=queryc(ls[x],ls[y],l,mid,L,R);
			if(R>mid) res+=queryc(rs[x],rs[y],mid+1,r,L,R);
			return res;
		}
		ll queryv(int x,int y,int l,int r,int L,int R)
		{
			if(L>R) return 0;
			if(L<=l && r<=R) return sum[y]-sum[x];
			int mid=(l+r)>>1;ll res=0;
			if(L<=mid) res+=queryv(ls[x],ls[y],l,mid,L,R);
			if(R>mid) res+=queryv(rs[x],rs[y],mid+1,r,L,R);
			return res;
		}
	}T;
}
using namespace Data_Structure;

namespace DreamLolita
{
	int n,Q;
	int a[N],f[N],lim[32][2];
	ll ans;
	void init()
	{
		n=read();
		for(int i=1;i<=n;++i) a[i]=read()^a[i-1];
		memset(lim,0x3f,sizeof(lim));
		for(int i=n;i;--i)
		{
			f[i]=n;
			for(int j=30;~j;--j) f[i]=min(f[i],lim[j][a[i-1]>>j&1]-1);
			for(int j=30;~j;--j) if((a[i]>>j&1)^(a[i-1]>>j&1)) {lim[j][a[i]>>j&1]=i;break;}	
		}
		for(int i=1;i<=n;++i) T.update(rt[i],rt[i-1],1,n,f[i]);
	}
	ll calc(ll x,ll y){return y*(y-1)/2-x*(x-1)/2;}
	void solve()
	{
		Q=read();
		while(Q--)
		{
			int l=(read()+ans)%n+1,r=(read()+ans)%n+1;
			ans=T.queryv(rt[l-1],rt[r],1,n,l,r)+1ll*r*T.queryc(rt[l-1],rt[r],1,n,r+1,n)-calc(l-1,r);
			writeln(ans);
		}
	}
	void solution(){init();solve();}
}

int main()
{
#ifdef Durant_Lee
	freopen("CC_PREFIXOR.in","r",stdin);
	freopen("CC_PREFIXOR.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}

你可能感兴趣的:(数据结构-线段树)