码迷,mamicode.com
首页 > 其他好文 > 详细

UVALive8518 Sum of xor sum

时间:2018-10-07 12:57:15      阅读:166      评论:0      收藏:0      [点我收藏+]

标签:其他   子序列   main   http   一个   tar   sum   区间   知识点   

题目链接:https://vjudge.net/problem/UVALive-8518

题目大意:

  给定一个长度为 $N$ 的数字序列 $A$,进行 $Q$ 次询问,每次询问 $[L,R]$,需要回答这个区间内的子序列的所有子序列的异或和之和。

  $1 \le N,Q \le 100000$

  $0 \le A[i] \le 1000000$

知识点:  前缀和

解题思路:

  将序列中的每一个数转成二进制(不超过 $20$ 位),逐位考虑。

  根据序列中的数字用二进制表示时在该位上为 $1$ 或 $0$,我们可以用 $20$ 个 $01$ 序列来表示序列 $A$。现在只考虑二进制位中的某一位,其他位做类似处理即可:

  先预处理出异或前缀和 $preXOR$(很明显,它是个 $01$ 序列),用 $zero[][i]$ 和 $one[][i]$ 表示在 $[1,i]$ 这个区间中的 $preXOR$ 有多少个 $0$  和 $1$。然后用 $sum[][i]$ 表示 $[1,i]$ 这个区间的答案。易知 $sum[][i] = sum[][i-1] + (所有以 A[i] 为右边界的子序列对答案的贡献)$,即 $sum[j][i] = sum[j][i-1] + 2^j *(preXOR[j][i]:zero[j][i-1]?one[j][i-1])$,因为只有当左边界的左边一位的异或前缀和和右边界的异或前缀和的异或和为 $1$ 时该区间才对答案有贡献。

  查询 $[L,R]$ 的时候,对于第 $j$ 位,对答案的贡献为:$sum[j][R]-sum[j][L-1]-zero[j][L-2]*(one[j][R]-one[j][L-1])*2^j -one[j][L-2]*(zero[j][R]-zero[j][L-1])*2^j$.

  后半部分其实是减掉那些左边界在 $[1,L-1]$ 而右边界在 $[L,R]$ 的区间对答案的影响,还是一样的道理: 只有当左边界的左边一位的异或前缀和和右边界的异或前缀和的异或和为 $1$ 时该区间才对答案有贡献。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 const int MAXN=100000+5;
 6 const int MOD=1e9+7;
 7 
 8 int A[MAXN];
 9 int preXOR[24][MAXN];
10 LL sum[24][MAXN],zero[24][MAXN],one[24][MAXN];
11 int main(){
12     int T;
13     scanf("%d",&T);
14     while(T--){
15         int N,Q;
16         scanf("%d%d",&N,&Q);
17         for(int i=1;i<=N;i++)   scanf("%d",&A[i]);
18         for(int i=0;i<20;i++){
19             preXOR[i][0]=0;
20             one[i][0]=0,zero[i][0]=1;
21             for(int j=1;j<=N;j++){
22                 if(A[j]&(1<<i))
23                     preXOR[i][j]=preXOR[i][j-1]^1;
24                 else
25                     preXOR[i][j]=preXOR[i][j-1];
26                 one[i][j]=one[i][j-1],zero[i][j]=zero[i][j-1];
27                 if(preXOR[i][j])    one[i][j]++;
28                 else                zero[i][j]++;
29             }
30         }
31         for(int i=0,now=1;i<20;i++,now<<=1){
32             sum[i][0]=0;
33             for(int j=1;j<=N;j++){
34                 sum[i][j]=sum[i][j-1];
35                 if(preXOR[i][j])
36                     sum[i][j]=(sum[i][j]+zero[i][j-1]*now)%MOD;
37                 else
38                     sum[i][j]=(sum[i][j]+one[i][j-1]*now)%MOD;
39             }
40         }
41         while(Q--){
42             int L,R;
43             scanf("%d%d",&L,&R);
44             LL ans=0;
45             for(int i=0,now=1;i<20;i++,now<<=1){
46                 ans=(ans+sum[i][R]-sum[i][L-1])%MOD;
47                 if(L>=2)
48                     ans=(ans-zero[i][L-2]*(one[i][R]-one[i][L-1])*now%MOD
49                          -one[i][L-2]*(zero[i][R]-zero[i][L-1])*now%MOD)%MOD;
50             }
51             if(ans<0)   ans+=MOD;
52             printf("%lld\n",ans);
53         }
54     }
55     return 0;
56 }

 

 

UVALive8518 Sum of xor sum

标签:其他   子序列   main   http   一个   tar   sum   区间   知识点   

原文地址:https://www.cnblogs.com/Blogggggg/p/9749707.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!