【暑训排位 #7 A】树状数组逆序对 异或求和 详解

给定长度为N的序列A1, A2, … AN,求

在这里插入图片描述

其中xor是按位异或运算。

题意:如题

思路:

1.如何对异或快速求和?
这个地方推了好久。我们从异或的性质入手,一串数两两异或下来,一个规律是不会比这串数的最大值还大的(毕竟不会多1),另一个是对于每个位,只有当相异的时候才会有正贡献(废话)。那我们就按位分析。
比如 111 101 100 (先看两两没有限制地异或,先不考虑Ai > Aj)
我们拿低一位来看,一共有2个1,1个0,他们匹配的时候就有2*1种得到贡献的可能(01搭配),那么低一位就有2个贡献,因为是到时要相加,那么这个位的总贡献20 * 2。同理,低二位1个1,2个0 ,有1 * 2 = 2的贡献,总贡献就乘上位权 = 21 * 2,同理最高位就是22 * 0 。
那么这个三个数两两异或能凑出来的异或和就是22 * 0 +21 * 2 + 20 * 2 = 4 + 2 = 6

2.解决了求和问题,那我们先看看逆序问题。求这个位置往前能组成多少个逆序对,我们直接树状数组就行。枚举当前位置i,看a[i]之前有逆序对共pair对,这个时候我们利用上面的规律,按位分析,当前位j是1的话,找到这pair个第j位有多少个0,这一点我们仍然可以用树状数组来维护。接着,假如pair个数的j位共有num个0,那可以和这个1组成num点贡献,乘上位权(1<<j)便是这个位的贡献,累加进答案。同理,如果这个位是0,就往前找这个位含有1的个数。这就完成了每一步的计数。

AC代码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值