第一问是可以O(n)的
二进制位是独立的,所以直接分开算即可。。
记一个前缀和,开桶装0、1的个数 然后每次新加入一个点就直接更新就可以了
第二问就比较难了,,手玩只能发现最低位可以这么搞,对于高位还要考虑借位情况,借位情况有0有1,就不好离散
但是这么做是可以做出来的。。只是要在模意义下进行,,再考虑每次-的过程,都是一个前缀和-比他靠前的前缀和,
当差值>枚举次数时,显然影响是1
差值要考虑进位的情况,,这样似乎就形成了一个偏序关系,,就可以用树状数组了。。
码(借鉴):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define P 998244353
ll ans,n,j,i,k,lin,er[50],he[50][100005],lie[100005],dui[100005],v[100005],a[100005],tong[100006],p[100005];
int lowbit(int a)
{
return a&(-a);
}
void jia(int o,int z)
{
for(;o<=dui[0];o+=lowbit(o))
{
v[o]^=z;
}
}
int cha(int o)
{
int ans=0;
for(;o;o-=lowbit(o))
{
ans^=v[o];
}return ans;
}
int find(ll o)
{
if(o<0)return 0;
int l=1,r=dui[0]+1;
while(l+1<r)
{
int mid=(l+r)>>1;
if(dui[mid]<=o)l=mid;
else r=mid;
}
return l;
}
int main()
{
er[0]=1;
for(i=1;i<=40;i++)
er[i]=er[i-1]*2;
scanf("%lld",&n);
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(i=0;i<=20;i++)
for(j=1;j<=n;j++)
{
he[i][j]=he[i][j-1]^(a[j]&er[i]);
if(he[i][j]&er[i])tong[i]++;
}
for(i=0;i<=20;i++)
{
for(j=1;j<=n;j++)
{
ans=1ll*(ans+1ll*tong[i]*er[i])%P;
if(a[j]&er[i])
{
tong[i]=(n-j)-(tong[i]-1);
}
}
}
printf("%lld ",ans);
ans=0;
for(i=1;i<=n;i++)he[0][i]=he[0][i-1]+a[i];
for(k=0;k<=37;k++){
for (i=0; i<=n; i++) tong[i]=p[i]=he[0][i]&(er[k+1]-1);
dui[0]=0;
sort(tong,tong+n+1); dui[++dui[0]]=tong[0];
memset(v,0,sizeof(v));
for (i=1; i<=n; i++)
if (tong[i]!=tong[i-1]) dui[++dui[0]]=tong[i];
int lin=0;
for (i=0; i<=n; i++){
lin^=cha(find(p[i]-er[k]))^cha(find(p[i]))^cha(find(p[i]+er[k]));
//cout<<er[k]<<" "<<lin<<" ";
jia(find(p[i]),1);
} //cout<<lin<<endl;
if (lin) ans|=er[k];
}
printf("%lld",ans);
}