题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=4017
第一问
xo[i]表示前 i个数的异或值, sum[i]表示前 i个数的和
第一问:用 cnt[k][0],cnt[k][1]表示第 k位 0的个数和 1的个数,朴素的做法是枚举 L和 R要 O(n2)
我们先枚举 R,如果 sum[R]的第 k位是 1,要是有个 sum[L−1]的第 k位是 0就好啦~,那我们的这个 sum[L R]区间的第 k位就是 1了,我们要做的就是统计出每枚举的一个 sum[R]有多少个 sum[L−1]与他异或是 1的,而且不需要知道具体是哪几个 L,只需要知道有多少个,那么个数就是 cnt里面保存的,是 1就找 0,是 0就找 1。
第二问
第二问其实就是bzoj 4888
这道题拖了好久,再学姐的帮助下终于过了T_T
bzoj 4888 题解
然后就是因为只用判断奇偶,所以就把树状数组优化成异或的形式了,很多大佬的博客都是这么写的,但是我还是比较喜欢比较有原始意义滴~所以就没有改成异或的那种
这道题的数据范围差不多是1e5*1e6=1e11,所以要用long long ,移位的时候注意用1LL
T_T
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
const int MOD=998244353;
int xo[maxn],a[maxn];
int tree[maxn];
LL sum[maxn],P[maxn];
int N,n;
LL Cnt[25][2];//极限数据1e5再左移20位就爆int了
LL solve1()
{
LL res=0;
memset(Cnt,0,sizeof(Cnt));
for(int k=0;k<=20;k++)
{
for(int r=1;r<=N;r++)
{
int t=1&(xo[r]>>k);
if(t==1)res+=1<<k;//相当于就他一个数这个区间
Cnt[k][t]++;
res+=Cnt[k][t^1]<<k;
res%=MOD;
}
}
return res;
}
void Add(int pos)
{
if(pos<0)return ;//有-1,所以要提前退出
pos++;//因为有0这个位置,所以树状数组的都加1
for(int i=pos; i<maxn; i+=i&-i)tree[i]++;;
}
int getsum(int pos)
{
pos++;
int res=0;
for(int i=pos; i>0; i-=i&-i)res+=tree[i];
return res;
}
int idx(LL x)//找最后一个小于等于他的数,所以是upper_bound-1
{
return upper_bound(P,P+n+1,x)-P-1;//注意,还有减1
}
LL solve2()
{
LL res=0;
for(int k=0; k<=40; k++)//差不多1e11,所以40位就差不多了
{
memset(tree,0,sizeof tree);
LL tp=0;
for(int i=1;i<=N;i++)P[i]=sum[i]%(1LL<<(1+k));
sort(P,P+1+N);
//unique这里弄出来是有n+1个数,但是我想把P[n]作为最后一个数,因此,还减了一个1
n=unique(P,P+1+N)-P-1;
for(int i=0;i<=N;i++)//这里要从0开始,我也不是很懂为什么
{
LL now=sum[i]%(1LL<<(k+1));
Add(idx(now));
tp+=getsum(idx(now-(1LL<<k)));//第一个不等式
tp+=getsum(idx(now+(1LL<<k)))-getsum(idx(now));//第二个不等式,求夹在中间的那一坨
}
if(tp&1LL)res|=(1LL<<k);
}
return res;
}
int main()
{
while(cin>>N)
{
for(int i=1;i<=N;i++)
{
scanf("%d",a+i);
sum[i]=sum[i-1]+a[i];
xo[i]=xo[i-1]^a[i];
}
cout<<solve1()<<" "<<solve2()<<endl;
}
}