Luogu3760 TJOI2017 异或和 树状数组

本文介绍了一种求解连续区间区间和异或值的高效算法。通过按位计算所有区间和中某一位上的1的个数,利用树状数组维护区间,实现O(nlognlog10^6)复杂度的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题意:给出一个长度为$N$的非负整数序列,求其中所有连续区间的区间和的异或值。$N \leq 10^5$,所有元素之和$\leq 10^6$


设序列的前缀和为$s_i$,特殊地,$s_0=0$

因为最后答案是一个异或值,所以我们考虑按位计算答案,也就是计算所有区间和中某一位上的$1$的个数。

考虑区间$[j+1,i]$在第$t$位上是否产生贡献,假设$s_i$的第$0$到$t-1$位的数字为$x$,$s_j$的第$0$到$t-1$位的数字为$y$,分类讨论:

①如果$s_i$的第$t$位为$1$,则$s_j$要么$t$位为$0$且$y<x$,要么$t$位为$1$且$y>x$

②如果$s_i$第$t$位为$0$,情况与上面相反。

可以发现不论什么情况,会产生贡献的$s_j$在$0-t$位上的取值表现为一段区间。于是使用树状数组维护这一段区间,每一次计算$s_i$对应的答案,然后把$s_i \text{& (1 << t) - 1}$丢入树状数组即可。复杂度$O(nlognlog10^6)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 inline int read(){
 5     int a = 0;
 6     char c = getchar();
 7     while(!isdigit(c))
 8         c = getchar();
 9     while(isdigit(c)){
10         a = (a << 3) + (a << 1) + (c ^ '0');
11         c = getchar();
12     }
13     return a;
14 }
15 
16 const int MAXN = 1e5 , MAXM = 2e6;
17 int treeNum[MAXM + 10];
18 int num[MAXN + 10] , N;
19 
20 inline int lowbit(int now){
21     return now & -now;
22 }
23 
24 inline void add(int now){
25     now++;
26     while(now < MAXM){
27         treeNum[now]++;
28         now += lowbit(now);
29     }
30 }
31 
32 inline int getSum(int now){
33     now++;
34     int sum = 0;
35     while(now){
36         sum += treeNum[now];
37         now -= lowbit(now);
38     }
39     return sum;
40 }
41 
42 int main(){
43     N = read();
44     int ans = 0;
45     for(int i = 1 ; i <= N ; i++)
46         num[i] = read() + num[i - 1];
47     for(int i = 0 ; i < 20 ; i++){
48         memset(treeNum , 0 , sizeof(treeNum));
49         int cnt = 0;
50         for(int j = 0 ; j <= N ; j++){
51             if(num[j] & (1 << i))
52                 cnt = (cnt + getSum((1 << i + 1) - 1) - getSum(num[j] & (1 << i + 1) - 1) + getSum(num[j] & (1 << i) - 1)) & 1;
53             else
54                 cnt = (cnt + getSum(((num[j] & (1 << i) - 1) | 1 << i)) - getSum(num[j] & (1 << i) - 1)) & 1;
55             add(num[j] & (1 << i + 1) - 1);
56         }
57         ans += cnt * (1 << i);
58     }
59     cout << ans;
60     return 0;
61 }

 

转载于:https://www.cnblogs.com/Itst/p/9825463.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值