4017: 小Q的无敌异或
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 694 Solved: 227
[ Submit][ Status][ Discuss]
Description
背景
小Q学习位运算时发现了异或的秘密。
描述
小Q是一个热爱学习的人,他经常去维基百科(http://en.wikipedia.org/wiki/Main_Page)学习计算机科学。
就在刚才,小Q认真地学习了一系列位运算符(http://en.wikipedia.org/wiki/Bitwise_operation),其中按位异或的运算符 xor 对他影响很大。按位异或的运算符是双目运算符。按位异或具有交换律,即i xor j = j xor i。
他发现,按位异或可以理解成被运算的数字的二进制位对应位如果相同,则结果的该位置为0,否则为1,例如1(01) xor 2(10) = 3(11)。
他还发现,按位异或可以理解成数字的每个二进制位进行了不进位的加法,例如3(11) xor 3(11) = 0(00)。
于是他想到了两个关于异或的问题,这两个问题基于一个给定的非负整数序列A1, A2, ..., An,其中n是该序列的长度。
第一个问题是,如果用f(i, j)表示Ai xor Ai+1 xor ... xor Aj,则任意的1 <= i <= j <= n的f(i, j)相加是多少。
第二个问题是,如果用g(i, j)表示Ai + Ai+1 + ... + Aj,则任意的1 <= i <= j <= n的g(i, j)异或在一起是多少。
比如说,对于序列{1, 2},所有的f是{1, 2, 1 xor 2},加起来是6;所有的g是{1, 2, 1 + 2},异或起来是0。
他觉得这两个问题都非常的有趣,所以他找到了你,希望你能快速解决这两个问题,其中第一个问题的答案可能很大,你只需要输出它对998244353(一个质数)取模的值即可。
Input
第一行一个正整数n,表示序列的长度。
第二行n个非负整数A1, A2, ..., An,表示这个序列。
Output
两个整数,表示两个问题的答案,空格隔开,其中第一个问题的答案要对998244353(一个质数)取模。
Sample Input
2
1 2
Sample Output
6 0
第一问:求出所有区间异或和的和
直接按位算贡献,如果一个区间中1的个数为奇数,那么这个区间对答案就有贡献
这个直接用前缀和就可以搞定了
第二问:求出所有区间和的异或和
先求出前缀和,假设区间[x, y]满足(sum[y]-sum[x-1])%2^(k+1)>=2^k,这样的区间总共有奇数个,那么答案的第k位就为1,否则就为0,将所有的sum[]对2^(k+1)取模后,如果存在x, y(x<y)满足
sum[y]-sum[x]>=2^k或者sum[y]-sum[x]>=-(2^k)&&sum[y]-sum[x]<0,那么区间[x, y]就满足上述条件,这个用树状数组就可以解决
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define mod 998244353
#define LL long long
int n, a[100005], tre[100005];
LL sum[100005], er[55] = {1};
typedef struct Res
{
LL x;
int id;
bool operator < (const Res &b) const
{
if(x<b.x || x==b.x && id<b.id)
return 1;
return 0;
}
}Res;
Res s[100005];
int Query(int x)
{
int ans = 0;
while(x)
{
ans += tre[x];
x -= x&-x;
}
return ans;
}
void Update(int k, int x)
{
while(k<=n)
{
tre[k] += x;
k += k&-k;
}
}
int main(void)
{
LL ans;
int i, j, A, B, now, p;
//freopen("in.txt", "r", stdin);
scanf("%d", &n);
for(i=1;i<=n;i++)
scanf("%d", &a[i]);
for(i=1;i<=45;i++)
er[i] = er[i-1]*2;
ans = 0;
for(i=0;i<=20;i++)
{
A = 1, B = 0;
now = 0;
for(j=1;j<=n;j++)
{
if(a[j]&(1<<i))
now ^= 1;
if(now==1)
{
ans = (ans+(LL)A*er[i])%mod;
B++;
}
else
{
ans = (ans+(LL)B*er[i])%mod;
A++;
}
}
}
printf("%lld", ans);
for(i=1;i<=n;i++)
sum[i] = sum[i-1]+a[i];
ans = 0;
for(i=0;i<=40;i++)
{
for(j=1;j<=n;j++)
s[j].x = sum[j]%er[i+1], s[j].id = j;
sort(s+1, s+n+1);
p = 1, now = 0;
memset(tre, 0, sizeof(tre));
for(j=1;j<=n;j++)
{
while(s[p].x+er[i]<=s[j].x && p<=j)
Update(s[p++].id, 1);
now = (now+Query(s[j].id))%2;
if(s[j].x>=er[i])
now ^= 1;
}
memset(tre, 0, sizeof(tre));
p = 1;
for(j=1;j<=n;j++)
{
while(s[p].x+er[i]<s[j].x && p<=j)
Update(s[p++].id, -1);
now = (now+(Query(n)-Query(s[j].id)))%2;
Update(s[j].id, 1);
}
if(now)
ans += er[i];
}
printf(" %lld\n", ans);
return 0;
}