题解
因为异或结果( ot=0)的情况过多,我们可以转为考虑结果(=0)的情况,用总数减去这些区间即可。每一个数都可以和包括自己的(n)个数组为区间,因此总数(=frac{n(n+1)}{2})。
至于如何求结果(=0)的方案数,可以想到前缀和,但枚举左右端点需要(O(n^2))的时间。进一步思考,我们只需要区间数量而不需要求出具体的区间。因为异或的逆运算是其本身,所以当(sum_{r}oplus sum_{l-1}=0)时区间([l,r])和为(0),因此对于每一个(r),(sum_{l-1}=sum_r)的(l)的个数即为右端点为(r)且区间异或和为(0)的区间个数,具体实现用(map[i])记录(sum=i)的元素个数即可。
此外还有变换操作,易得该操作的实质是(a_ioplus 2^k-1)。对于(a_i),在变换与不变换中取最小值即可。
又及:当前缀和(0)的时候是不可以取([1,r])区间的,因此(map[0]=1)(WA的教训)。
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
map<int,int> mp;
signed main()
{
int n,k,lst=0,ans=0;
scanf("%lld%lld",&n,&k);
int mov=(1<<k)-1; mp[0]=1;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
int qwq=lst^a[i]; int qaq=qwq^mov;//qwq:不变换,qaq:变换
if(mp[qwq]<mp[qaq]) {ans+=(mp[qwq]++); lst=qwq;}//不变换更优
else {ans+=(mp[qaq]++); lst=qaq;}//变换更优
}
printf("%lld",n*(n+1)/2-ans);
return 0;
}