【题目】
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 n≤4×105,ai≤109
【解题思路】
根据套路,我们首先肯定是求一个前缀异或和,然后要求 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} sj⊕si−1≥sj−1⊕si−1。
一个可能可行的思路是我们求出对于每个 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=l∑r(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;
}