原题传送门
手玩数据
以5为例
00000101
比5大的最小的能和5匹配的是11(00001011)
再大,就是
00011011
00111011
01111011
11111011
……
再假如10
00001010
与之匹配可以是
00010110(22)
00110110(54)
01110110
11110110
……
结论,对于一个数
x
x
x,另一个数
y
>
=
x
y>=x
y>=x
x
+
y
=
2
p
x+y=2^p
x+y=2p
y
y
y一定是
x
x
x最后一位1不动,之前全部取反,然后最高位想补多少1就补多少
发现一个数可以有很多比自己大的与之匹配的数
但是一个数只有一个比自己小的与之匹配的数(显然)
然后就可以用这个结论进行贪心
先把重复的数合并,再从大到小枚举
对于当前的数
x
x
x,若存在一个数
y
>
x
,
x
和
y
匹
配
y>x,x和y匹配
y>x,x和y匹配,且没有被用掉,那么这个
y
y
y只可能跟
x
x
x匹配了,不会再有更小的的数与
y
y
y匹配,所以直接把
x
和
y
x和y
x和y匹配一定是最优的
因为一个
x
x
x存在很多匹配的
y
y
y,这样的
y
y
y也要从最大开始枚举才是正确的
然后判断是否存在可以用二分
位运算复杂度是
O
(
l
o
g
n
)
O(logn)
O(logn)
二分也是
O
(
l
o
g
n
)
O(logn)
O(logn)
所以总复杂度是
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)
Code :
#include <bits/stdc++.h>
#define maxn 200010
#define LL long long
using namespace std;
struct data{
LL val;
int cnt;
}b[maxn];
LL a[maxn], ans, power[50];
int n, m;
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
int find(LL basis, int pos){
int l = pos, r = m, ans = 0;
while (l <= r){
int mid = (l + r) >> 1;
if (b[mid].val >= basis) r = mid - 1; else l = mid + 1;
if (b[mid].val == basis) return ans = mid;
}
return ans;
}
int main(){
freopen("Miyako.in", "r", stdin);
freopen("Miyako.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i) a[i] = read();
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; ++i){
if (a[i] != a[i - 1]) b[++m].val = a[i], b[m].cnt = 1;
else ++b[m].cnt;
}
power[0] = 1;
for (int i = 1; i <= 32; ++i) power[i] = power[i - 1] << 1;
b[0].val = -1;
// for (int i = 1; i <= m; ++i) printf("%d %d\n", b[i].val, b[i].cnt);
for (int i = m; i; --i){
LL x = b[i].val;
LL y = 0, flag = 0;
int k = 0;
for (; ; ++k){
if (power[k] > x) break;
if (x & power[k]){
if (!flag) y += power[k];
flag = 1;
} else
if (flag) y += power[k];
}
// printf("%lld\n", y);
if (k > 30) continue;
// if (y < x) y += power[k++];
while (y + power[k] <= b[m].val) y += power[k++];
while (y >= x && b[i].cnt > 0){
// printf(" %lld\n", y);
int p = find(y, i);
if (b[p].val == y){
if (x == y){
ans += b[i].cnt / 2;
b[i].cnt &= 1;
} else{
int sum = min(b[p].cnt, b[i].cnt);
b[i].cnt -= sum, b[p].cnt -= sum;
ans += sum;
}
}
if (b[i].cnt == 0) break;
y -= power[--k];
}
}
printf("%d\n", ans);
return 0;
}