计蒜客 - A1613 UVALive - 8518  

Sum of xor sum

这两题是一样的,但后面那个数据为空的,你输出“下次一定”都能过

 我们要求的就是给你任意区间[L,R],能得出这么一个东西

区间异或和的和

,直接从数的本身下手,是没有想法的。异或这个位操作有关的,我们可以从二进制位来考虑,如果我们知道了每一位对答案的贡献,那么最后直接把所有位的答案再加起来即可。

这里我们对j位进行讨论,用sum[i-1][j]来表示,前i-1个数在二进制位置j的答案前缀和,当在加多一个数a[i],怎么得到sum[i][j]呢?

首先,当加多a[i]之后,多的区间自然是[1,i],[2,i]...[3,i]以i为右边界的区间,这时我们再看哪些区间对答案有贡献。

用xr[i][j]表示前i个数在j位置的异或和,那对答案有贡献的区间情况无非两种,设区间为[k,i],便是xr[k][j]=0,xr[i][j]=1跟xr[k][j]=1,xr[i][j]=1。

因为xr[k][j]跟xr[i][j]相同的话,不就表示着j位在(k,i]区间内有偶数个1,这偶数个1异或为0,那(k,i]也就是[k+1,i]区间对答案自然没有贡献。

由此我们可知,当xr[i][j]=1时,对答案有贡献的区间左端点k便是xr[k][j]=0,而xr[i][j]=0时则相反。

所以这时候我们要用cnt0[i][j],来记录前i个数在j位置的异或和为0的数有多少个,cnt1[i][j]同理。明显初始值,cnt0[0][j]=1,cnt1[0][j]=0。

那么sum[i][j]=sum[i-1][j]+2j*(xr[i][j] ? cnt0[i-1][j] : cnt1[i-1][j]); (2j为j为的权重),也就是在[0,i-1]中找一个左端点k,使得(k,i]中有奇数个1。

要求区间[L,R]内的答案,自然是对每一位求ans+=sum[R]-sum[L-1],j位区间右端点在[L,R]范围中的答案

但此时还多算了点答案,也就是左端点在[0,L-1],而右端点在[L,R]中的答案,所以还得把这部分减去

ans-=2j*cnt0[L-2][j]*(cnt1[R][j]-cnt1[L-1][j]),ans-=2j*cnt1[L-2][j]*(cnt0[R][j]-cnt0[L-1][j])

为什么是L-2呢,看在[0,i-1]中找一个左端点k,使得(k,i]中有奇数个1那里,我们的区间是左开右闭的。

如果是L-1的话,就变成了(L-1,L~R]=[L,L~R]多减去了左端点为L的情况。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+11,M=21,md=1000000007;
int cf2[N],sum[N][M],xr[N][M],cnt[3][N][M];
int main(){
    int t,n,q,x;
    cf2[0]=1;
    for(int i=0;i<M;i++){
        if(i) cf2[i]=cf2[i-1]<<1;
        cnt[0][0][i]=1;
        cnt[1][0][i]=0;
    }
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            for(int j=0;j<M;j++){
                int pw=(x>>j)&1;
                pw=xr[i][j]=xr[i-1][j]^pw;
                cnt[0][i][j]=cnt[0][i-1][j]+(pw^1);
                cnt[1][i][j]=cnt[1][i-1][j]+pw;
                sum[i][j]=sum[i-1][j]+1ll*cf2[j]*cnt[pw^1][i-1][j]%md;
                if(sum[i][j]>=md) sum[i][j]-=md;
            }
        }
        int l,r;
        while(q--){
            scanf("%d%d",&l,&r);
            long long ans=0;
            for(int i=0;i<M;i++){
                ans+=sum[r][i]-sum[l-1][i];
                ans=(ans%md+md)%md;
                if(l>=2){
                    ans-=1ll*cf2[i]*cnt[0][l-2][i]%md*(cnt[1][r][i]-cnt[1][l-1][i])%md;
                    ans=(ans%md+md)%md;
                    ans-=1ll*cf2[i]*cnt[1][l-2][i]%md*(cnt[0][r][i]-cnt[0][l-1][i])%md;
                    ans=(ans%md+md)%md;
                }
            }
            printf("%lld\n",ans);
        }
    } 
    return 0;
}
下次一定

相关文章: