A,B签到,C整数分块简单变形
D.优化枚举+手写hash
题目大意:
给你两个集合。问你两个集合中有多少个数对,它们二进制中恰好有两位不同.
n
≤
1
e
5
,
a
i
<
2
30
n \leq 1e5,a_i< 2^{30}
n≤1e5,ai<230
题目思路:
将一个集合丢入hash,枚举另一个集合,暴力枚举两个不同的位,然后查询。复杂度: O ( 30 ∗ 30 ∗ n ) O(30*30*n) O(30∗30∗n).
考虑优化。这么思考这个问题,我们先修改第一个集合里数的一个二进制位,然后对第二个集合,我们同样也修改一个二进制位,然后查询相等的对数。所以做法就是:对第一个集合每个数枚举修改一个不同的位,丢入hash.然后对第二个集合枚举另一个不同的位。查询即可。 还要注意 可能修改的是同一位,所以得容斥掉值同时存在于两个集合的情况。然后修改的方案也是有两种。所以最终答案要除2.如下图:
复杂度: O ( 30 ∗ n ) O(30*n) O(30∗n)
但是这题卡unordered_map(具体原理见Neal大佬的cf博客),所以得手写hash。模板如下:
模板一:
模数:
1000011
(
非
质
数
)
1000011(非质数)
1000011(非质数),运行时间:
740
m
s
740ms
740ms
模数:
2000003
(
质
数
)
2000003(质数)
2000003(质数),运行时间:
400
m
s
400ms
400ms
模数:
9000011
(
质
数
)
9000011(质数)
9000011(质数),运行时间:
290
m
s
290ms
290ms
理论上模数越大,且是质数,时间复杂度就比较低
定址方法:除留余数法
解决冲突的方法:拉链法(链式向前星)
struct HashSet {
static const int N = 9e6 + 11;
struct node {
int k, v, nex;
} buf[N];
int h[N], tot, mod = N;
void add(int x) {
int pos = x % mod;
for (int i = h[pos]; i; i = buf[i].nex) {
if (buf[i].k == x) { buf[i].v++; return; }
}
buf[++tot] = { x, 1, h[pos] };
h[pos] = tot;
}
int ask(int x) {
int pos = x % mod;
for (int i = h[pos]; i; i = buf[i].nex) {
if (buf[i].k == x) return buf[i].v;
}
return 0;
}
}s;