概述
本文首发于我的blog,欢迎点击查看(无广告界面清爽!)
题目网址: https://codeforces.com/contest/1322/problem/B
写这篇博文是因为第一次遇到这个解法,对于我这个算法小白来说还是很新颖的。
PS.做题的时候天真的以为是O(n)的解法,并且可以用数学做。。。
题目
给定
n
n
n个数
a
1
a_1
a1,
a
2
a_2
a2, … ,
a
n
a_n
an,
计算其两两之和的异或值。
分析
首先输入大小以看就不可以直接求(废话)
复杂度也就是压在O(nlogn)左右
关键点
我们可以把答案回到二进制,然后对二进制的每一位分开求。
原理
首先我们定义
x
i
=
a
k
+
a
j
x_i = a_k + a_j
xi=ak+aj, 即
x
i
x_i
xi 是两数之和。
令
s
s
s 是答案,
s
i
s_i
si 是指从右往左答案的第
i
i
i 个二进制位(从0开始)
我们现在假设计算
s
i
s_i
si,我们已知所有
x
x
x 的第
i
i
i 位的01情况,那么求
s
i
s_i
si 就是对它们做异或,也就等于计算
x
x
x 中第
i
i
i 位是1的个数的奇偶情况。
即如果1有偶数个,那么答案对应的位数是0,反之则是1。(简单的异或运算原理)
Subproblem
对于结果 s s s,计算第 i i i 位的值,即 s i s_i si。
(等价问题)计算两数之和 x x x的第 i i i 位是 1 1 1 的数量。
对于这个等价问题,我们先思考一个简单的情况:
假设现在有一个数
y
<
2
i
+
1
y < 2^{i+1}
y<2i+1,它的第
i
i
i位的是否位
1
1
1取决于它是否在
[
2
i
,
2
i
+
1
)
[2^{i}, 2^{i+1})
[2i,2i+1) 内。
现在把
y
y
y 扩大为正数,我们可以得到其第
i
i
i位的情况与 $y’ = y % 2^{i+1} $。
因此,对于每个 x x x,我们可以只考虑 $x % 2^{i+1} $。
但我们希望能通过
a
a
a 直接计算出个数,而非计算
x
x
x 后求出解。
既然我们可以对
x
x
x 取模,自然也可以对
a
a
a 取模,这对加法并不会有影响。
因此,我们首先将
a
a
a 对
2
i
+
1
2^{i+1}
2i+1 取模,令取模后的数为
b
b
b。
我们可以得到
b
∈
[
0
,
2
i
+
1
)
b in [0, 2^{i+1} )
b∈[0,2i+1),
即有
x
∈
[
0
,
2
i
+
2
−
1
)
x in [0, 2^{i+2}-1 )
x∈[0,2i+2−1)。
我们发现
x
x
x 的范围缩小了很多,这次我们不对
x
x
x 取模,
相反,我们可以直接对
x
x
x 划出两个范围:
[
2
i
,
2
i
+
1
)
[2^i, 2^{i+1})
[2i,2i+1) 和
[
2
i
+
2
i
+
1
,
2
i
+
2
−
1
)
[2^i + 2^{i+1}, 2^{i+2}-1 )
[2i+2i+1,2i+2−1)
因此,我们希望寻找 x x x 在这两个范围内的个数
Subsubproblem
对于结果 s s s 的第 i i i 位,现有 b b b 是对 a a a 对于 2 i + 1 2^{i+1} 2i+1 取模,求 b b b 两两之和在 [ 2 i , 2 i + 1 ) [2^i, 2^{i+1}) [2i,2i+1) 和 [ 2 i + 2 i + 1 , 2 i + 2 − 1 ) [2^i + 2^{i+1}, 2^{i+2}-1 ) [2i+2i+1,2i+2−1) 的个数。
(简化版)给定数组 b b b,求其两两和在某一区间的个数。
这里为简化,假设求的区间是 [ k , t ) [k, t) [k,t)
暂时只有 O ( n l o g n ) O(nlogn) O(nlogn) 的解法,未知是否有更迅速的
首先将
b
b
b 从小到大排序,
然后对
b
i
b_i
bi ,它可以相加的数为
b
i
+
1
b_{i+1}
bi+1, …,
b
n
b_n
bn 。
用二分查找寻找下界值为
k
−
b
i
k-b_i
k−bi 的下标
p
p
p,
用二分查找寻找上界值为
t
−
b
i
t-b_i
t−bi 的下标
q
q
q。
满足的个数就是
q
−
p
q-p
q−p。
代码
#include <bits/stdc++.h>
using namespace std;
long long a[400005], b[400005];
int main()
{
ios::sync_with_stdio(false);
long long n, i, j, k, t, m, p, q, s;
cin >> n;
m = 0;
for (i = 0; i < n; i++) {
cin >> a[i];
m = max(a[i], m);
}
m = log2(m) + 2;
s = 0;
for (i = 0; i <= m; i++) {
k = pow(2, i+1);
for (j = 0; j < n; j++) {
b[j] = a[j] % k;
}
sort(b, b+n);
t = 0;
for (j = 0; j < n; j++) {
// 2^i, 2^(i+1)
p = lower_bound(b+j+1, b+n, k/2 - b[j]) - b;
q = lower_bound(b+j+1, b+n, k - b[j]) - b;
t += q - p;
// 2^i + 2^(i+1), 2^(i+2)-1
p = lower_bound(b+j+1, b+n, k/2*3 - b[j]) - b;
q = lower_bound(b+j+1, b+n, 2*k - 1) - b;
t += q - p;
}
if (t % 2 == 1) {
s += k / 2;
}
}
cout << s << endl;
return 0;
}
最后
以上就是狂野绿草为你收集整理的Codeforces——1322B.Present题目分析代码的全部内容,希望文章能够帮你解决Codeforces——1322B.Present题目分析代码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复