原题地址:http://codeforces.com/gym/100307/problem/H
题意:
给定一个序列,求序列的子区间中,满足子区间XOR值等于AND值得子区间个数。
题解:
一直以为NEERC这种有名的比赛应该题解到处都是,太天真了……
首先考虑区间的AND值。
对于固定起点的区间,因为 & 的性质,AND值必然单调递减,并且,根据AND值中1的个数划分后面的区间,必然最多只能划分成32段,对于某一段中的任意位置,它和固定的起点所形成的子区间的AND值是一样的。
再来考虑区间的XOR值
对于数列A[],定义sum(a,b)=A[a]^A[a+1]^...^A[b];
那么sum(a,b)=(A[1]^A[2]^...^A[a-1])^((A[1]^A[2]^...^A[a-1])^(A[a]^A[a+1]^...^A[b])=sum(1,a-1)^sum(1,b),这是一个前缀和形式
考虑题给的等式,AND(a,b)==XOR(a,b)
也就是AND(a,b)==XOR(1,a-1)^XOR(1,b)
也就是AND(a,b)^XOR(1,a-1)==XOR(1,b) ——(1)
我们枚举区间的起点,XOR(1,a-1)是定值,接下来枚举AND值相同的区段,最多32段,在每一段中寻找满足等式(1)的b,累计答案
区间AND值可以用RMQ思想处理,区段的划分可以二分处理,而寻找区段中XOR值为固定值的元素个数,可以转化为前缀和
#include<bits/stdc++.h> #define clr(x,y) memset((x),(y),sizeof(x)) using namespace std; typedef long long LL; struct seg { int l; int val; }; const int maxn=1e5; int AND[maxn+5][18]; int XOR[maxn+5]; int n; int A[maxn+5]; vector <seg> v[maxn+5]; //段划分 map <int,vector<int> > mp; //mp[x]中储存XOR[1,a]==x的元素下标a void build() //计算区间AND值 { for (int i=1;i<=n;++i) AND[i][0]=A[i]; for (int j=1;(1<<j)<=n;++j) { for (int i=0;i+(1<<j)-1<=n;++i) { AND[i][j]=AND[i][j-1] & AND[i+(1<<(j-1))][j-1]; } } } int calu(int l,int r) { int k=0; while ((1<<(k+1))<=r-l+1) ++k; return AND[l][k] & AND[r-(1<<k)+1][k]; } LL Count(int l,int r,int val) //区间[l,r]中XOR值为val的元素个数 { return upper_bound(mp[val].begin(),mp[val].end(),r)-lower_bound(mp[val].begin(),mp[val].end(),l); } void solve() { ++n; A[n]=0;//为了便于划分在末尾加上一个0 build(); for (int i=1;i<=n;++i) v[i].clear(); for (int i=1;i<=n;++i) { int val=A[i]; int l=i; int r=n; v[i].push_back((seg){l,val}); while (val!=0) //不断二分划分段 { int lb=l; int ub=r; while (ub-lb>1) { int mid=(ub+lb)>>1; if (calu(i,mid)==val) { lb=mid; } else { ub=mid; } } l=ub; val=calu(i,l); v[i].push_back((seg){l,val}); } } mp.clear(); XOR[0]=0; for (int i=1;i<=n;++i) { XOR[i]=XOR[i-1]^A[i]; mp[XOR[i]].push_back(i); } LL ans=0; for (int i=1;i<=n;++i) { for (int j=0;j<v[i].size();++j) { int l=v[i][j].l; int r=(j==v[i].size()-1)? n:v[i][j+1].l-1; int val=v[i][j].val^XOR[i-1]; ans+=Count(l,r,val); //printf("%d %d %d ",l,r,val); } } //消除最后那个虚拟的0对答案的影响 LL extra=1; int xor_tmp=0; for (int i=n-1;i>=1;--i) { xor_tmp^=A[i]; if (xor_tmp==0) ++extra; } printf("%lld ",ans-extra); } int main(void) { #ifdef ex freopen ("../in.txt","r",stdin); //freopen ("../out.txt","w",stdout); #endif freopen("hack.in","r",stdin); freopen("hack.out","w",stdout); while (scanf("%d",&n)==1) { for (int i=1;i<=n;++i) { scanf("%d",&A[i]); } solve(); } }