abcdeffa's Blog

当局者迷,旁观者清。

0%

GMOJ S5258 【友好数对】

Description

定义一个数 $S$ 在二进制下数值为 1 的位数的个数为 $cnt(S)$。

现在给出你两组数 $\{a_1,a_2,…,a_n\}$ 和 $\{b_1,b_2,…,b_m\}$。

请你求出 $\sum_{i = 1} ^ {n} \sum_{j = 1} ^ {m} [cnt(a_i ⊕ b_j) = 2]$ 的值。

Solution

考虑一个比较暴力的做法:先把 $b_j$ 放进哈希表里,然后暴力枚举 $a_i$ 改了哪两位,再到哈希表里找,统计个数和即可。

但这样有一个 900 的常数,想要卡过去有点难度,考虑进行卡常。

优化:把 $b_j$ 改一位的结果丢进哈希表里,然后暴力枚举 $a_i$ 改了哪一位,再到哈希表里找,统计个数和,最后减去 $a_i = b_j$ 的情况再除 2 即可。

这样的话常数大概就是 30 + 30 = 60 了,比较容易卡过去。

我写的是离散化,复杂度多了一个 $\log$,开 O2 后卡过去了。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#pragma GCC optimize(2)
#include <cstdio>
#define maxN 3000010
int pow[50];
int f[maxN], g[maxN];
int a[maxN], b[maxN], c[maxN], d[maxN];
int read ()
{
int x = 0;
char c = getchar();
while(c < '0' || c > '9')
{
c = getchar();
}
while(c >= '0' && c <= '9')
{
x = x * 10 + (c - '0');
c = getchar();
}
return x;
}
void px (int p[], int l, int r)
{
int x = l, y = r, mid = p[(l + r) >> 1];
while(x <= y)
{
while(p[x] < mid)
{
x++;
}
while(p[y] > mid)
{
y--;
}
if(x <= y)
{
int t = p[x];
p[x] = p[y];
p[y] = t;
x++;
y--;
}
}
if(l < y)
{
px(p, l, y);
}
if(x < r)
{
px(p, x, r);
}
}
int find (int x)
{
int l = 1, r = c[0];
while(l < r)
{
int mid = (l + r) >> 1;
if(c[mid] < x)
{
l = mid + 1;
}
else
{
r = mid;
}
}
return c[l] != x ? 0 : l;
}
int main ()
{
int n = 0, m = 0;
n = read();
m = read();
for(int i = 1;i <= 30; i++)
{
pow[i] = (1 << (i - 1));
}
for(register int i = 1;i <= n; i++)
{
a[i] = f[i] = read();
}
for(register int i = 1;i <= m; i++)
{
g[i] = read();
for(register int j = 1;j <= 30; j++)
{
b[++b[0]] = (g[i] ^ pow[j]);
}
}
px(b, 1, b[0]);
c[++c[0]] = b[1];
d[c[0]] = 1;
for(register int i = 2;i <= b[0]; i++)
{
if(b[i] != b[i - 1])
{
c[++c[0]] = b[i];
}
d[c[0]]++;
}
long long Ans = 0;
for(register int i = 1;i <= n; i++)
{
for(register int j = 1;j <= 30; j++)
{
Ans += d[find(a[i] ^ pow[j])];
}
}
px(f, 1, n);
px(g, 1, m);
int j = 1;
int cnt = 0;
for(register int i = 1;i <= n; i++)
{
cnt = 1;
while(f[i] == f[i + 1] && i + 1 <= n)
{
i++;
cnt++;
}
while(g[j] < f[i] && j <= m)
{
j++;
}
while(g[j] == f[i] && j <= m)
{
Ans -= 30 * cnt;
j++;
}
if(j > m)
{
break;
}
}
printf("%lld", Ans / 2);
return 0;
}