洛谷3917 异或序列
原题链接
按每个数的二进制下一位一位计算贡献。
对于第\(k\)位,计算前缀异或和\(S[1\to n]\ (S[0] = 0)\),则求这一位所能产生贡献的次数即是求有多少区间满足异或值为\(1\),即求有多少\([i,j]\ (1\leqslant i\leqslant j \leqslant n)\)满足\(S[i - 1] \oplus S[j] = 1\ (\oplus \text{表示异或})\)。
有结论:满足要求的区间个数等于\(S[0\to n]\)中\(1\)的个数与\(0\)的个数的乘积。
而对于第\(k\)位,单个区间所产生贡献值为\(2^k\)。
最后将其所有贡献累加起来即可。
时间复杂度\(O(n\times log_2MAXNUM)\)。
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int a[N];
inline int re()
{
int x = 0;
char c = getchar();
bool p = 0;
for (; c < '0' || c > '9'; c = getchar())
p |= c == '-';
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return p ? -x : x;
}
inline int maxn(int x, int y)
{
return x > y ? x : y;
}
int main()
{
int i, j, n, ma = 0, o, k, s_1, s_0;
ll s = 0;
n = re();
for (i = 1; i <= n; i++)
{
a[i] = re();
ma = maxn(ma, a[i]);
}
o = log2(ma);
for (j = 0; j <= o; j++)
{
s_1 = k = 0;
s_0 = 1;
for (i = 1; i <= n; i++)
{
k ^= (a[i] >> j) & 1;
k ? s_1++ : s_0++;
}
s += 1LL * s_1 * s_0 * (1 << j);
}
printf("%lld", s);
return 0;
}
posted on 2018-10-22 12:52 Iowa_Battleship 阅读(191) 评论(0) 编辑 收藏 举报