题目大意
给出 n n n个数 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots,a_n a1,a2,⋯,an,每个数在二进制下,可以任意交换0和1的位置(也可以不交换),问有多少个区间 [ l , r ] [l,r] [l,r]满足在每个数在操作之后的异或和为0。
时间限制
2s
数据范围
n
≤
3
×
1
0
5
n\le 3\times 10^5
n≤3×105
a
i
≤
1
0
18
a_i\le 10^{18}
ai≤1018
题解
不难发现,只需要关心这个数在二进制下1的个数,而不要具体关注这个数是什么。
那么考虑一下满足条件的区间中1的个数,
显然1的个数一定是偶数,不然1无法两两匹配在异或的时候消去。
除了这个条件,还有没有别的条件,
显然是有的,来看这种情况:
两个相邻的数,一个数有31个1,另一个数只有1个1,它们的和是偶数,但是它们并不满足条件。
因此多了一个条件,这个区间其它数1的个数的和要比的最多1的那个数要大。
不难发现,这个最大值只有60,对于这部分情况可以之间枚举,其余的情况通过前缀和计算。
Code
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#define ll long long
#define G getchar
using namespace std;
ll read()
{
char ch;
for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
ll n = 0 , w;
if (ch == '-')
{
w = -1;
ch = G();
} else w = 1;
for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
return n * w;
}
void write(int x)
{
if (x > 9)
{
write(x / 10);
putchar(x % 10 + 48);
}
else putchar(x + 48);
}
const int N = 300005;
int n , s[N] , num[N][3] , tmp , mx , a[N];
ll ans , x , z[63];
int main()
{
//freopen("d.in","r",stdin);
//freopen("e.out","w",stdout);
z[0] = 1;
for (int i = 1 ; i < 63 ; i++)
z[i] = z[i - 1] << 1;
n = read();
num[0][0] = 1;
memset(a , 0 , sizeof(a));
for (int i = 1 ; i <= n ; i++)
{
x = read();
for (int j = 0 ; z[j] <= x ; j++)
if (z[j] & x) a[i]++;
s[i] = (s[i - 1] + a[i]) % 2;
num[i][0] = num[i - 1][0];
num[i][1] = num[i - 1][1];
num[i][s[i]]++;
mx = tmp = a[i];
for (int j = i - 1 ; j ; j--)
{
if (tmp > 120)
{
ans = ans + num[j - 1][s[i]];
break;
}
tmp = tmp + a[j];
mx = max(mx , a[j]);
if (tmp >= mx * 2 && tmp % 2 == 0) ans++;
}
}
printf("%lld\n", ans);
return 0;
}