给你一些数,然后要你把它们放进一个哈希表里面。
要你选一个哈希表的大小,使得每个数在哈希表发现没有位置向后移动放置位置的次数尽可能的多。
输出这个最大的次数。
鱼贯而入
题目大意
给你一些数,然后要你把它们放进一个哈希表里面。
要你选一个哈希表的大小,使得每个数在哈希表发现没有位置向后移动放置位置的次数尽可能的多。
输出这个最大的次数。
思路
首先我们考虑一个问题,假设我们枚举了哈希表的大小,如何用一种方法快速地求出次数。
如果长度小,我们可以用哈希暴力枚举搞。
如果大了,我们可以用另一个哈希把下表给离散。
然后就是哈希里面再套一个哈希。
然后你考虑要丢那些数进去算。
首先有一些数根本就不会发生重叠,那我们可以把这些数给排除掉。
那如果 x,y 在长度为 len 的哈希表中要有重叠,那它们模 len 余数要相同,就是 len| |x−y|
那枚举每两个数之间的差,然后把它们的约数全部丢进来看一次。
但你发现你求约数个数太多,而且你求的过程就会炸。
考虑先处理约数个数太多的问题。
那你考虑 len 和 xlen,发现 len 一定会有 xlen 的寻址时间。
那就是说,如果这两个都可以,那 len 一定更优。
那我们就只需要找这些因子:它大于等于 n,但它除去它的最小质因子小于 n。
那我们考虑有哪些满足,那大于等于 n 的质数一定可以,而且剩下有可能的一定会在 n∼n2 中。
为什么呢?那我们假设它除去它最小质因子尽可能小,那到最小就是它可以表示成 x2 形式,那它除去它的的最小质因子还是它的最小质因子,那这个数 x 被规定小于 n,那就得到 x<n。
那 x2<n2,所以这个数不过超过 n2。
那就知道要找那些数了,先是 n∼n2 的直接一股脑全部丢进去,反着才三万多,不多不多。
接着是讲每个 |ai−aj| 质因数分解得出来大于 n 的质数。
接着就是怎么分解出质因数。
首先可能会有重复的,跟前面一样拿哈希判断。
然后质因数分解用 Miller Rabin 加 Pollard Rho 就可以了。
代码
#include<cstdio>
#include<cstdlib>
#include<iostream>
#define ll long long
#define Mo 1145141
using namespace std;
int tp, n, v[201], ans, sum, pl[201], nn;
ll a[201], h[1145141];
ll hs[2][1145141], ask[200001];
ll Abs(ll x) {
return x < 0 ? -x : x;
}
ll gcd(ll x, ll y) {
ll tmp;
while (y) {
tmp = x;
x = y;
y = tmp % y;
}
return x;
}
ll ksc(ll x, ll y, ll mo) {
x %= mo; y %= mo;
ll c = (long double)x * y / mo;
long double re = x * y - c * mo;
if (re < 0) re += mo;
else if (re >= mo) re -= mo;
return re;
}
ll ksm(ll x, ll y, ll mo) {
ll re = 1;
while (y) {
if (y & 1) re = ksc(re, x, mo);
x = ksc(x, x, mo);
y >>= 1;
}
return re;
}
int add(ll now, ll *hash) {
int x = now % Mo;
while (hash[x] && hash[x] != now) {
x++;
if (x == Mo) now = 0;
}
return x;
}
void addfish(int i, ll p) {
ll x = a[i] % p;
int y = add(x + 1, hs[0]);
while (hs[0][y] && hs[1][y] != a[i]) {
x++; if (x == p) x = 0;
y = add(x + 1, hs[0]);
sum += v[i];
}
hs[0][y] = x + 1; hs[1][y] = a[i];
pl[i] = y;
}
bool mr(ll x, ll p) {
if (ksm(x, p - 1, p) != 1) return 0;
ll y = p - 1, z;
while (!(y & 1)) {
y >>= 1;
z = ksm(x, y, p);
if (z != 1 && z != p - 1) return 0;
if (z == p - 1) return 1;
}
return 1;
}
bool prime(ll now) {
if (now < 2) return 0;
if (now == 2 || now == 3 || now == 5 || now == 7 || now == 43) return 1;
return mr(2, now) && mr(3, now) && mr(5, now) && mr(7, now) && mr(43, now);
}
ll pro(ll p) {
ll x, y, c, z, g;
int i, j;
while (1) {
x = y = rand() % p;
z = 1;
c = rand() % p;
i = 0; j = 1;
while (++i) {
x = (ksc(x, x, p) + c) % p;
z = ksc(z, Abs(y - x), p);
if (x == y || !z) break;
if (!(i % 127) || i == j) {
g = gcd(z, p);
if (g > 1) return g;
if (i == j) {
y = x;
j <<= 1;
}
}
}
}
}
void work(ll now) {
if (now < nn) return ;
int x = add(now, h);
if(h[x]) return ; h[x] = now;
if (prime(now)) {
ask[++ask[0]] = now;
return ;
}
ll l = pro(now);
while (now % l == 0) now /= l;
work(l); work(now);
}
int main() {
srand(19491001);
scanf("%d %d", &tp, &n);
nn = n;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
v[i] = 1;
for (int j = 1; j < i; j++)
if (a[j] == a[i]) {
v[j]++;
n--;
i--;
break;
}
}
for (int i = nn; i <= nn * nn; i++)
ask[++ask[0]] = i;
for (int i = 1; i <= n; i++)
for (int j = 1; j < i; j++)
work(Abs(a[j] - a[i]));
for (int i = 1; i <= ask[0]; i++) {
ll now = ask[i];
sum = 0;
for (int j = 1; j <= n; j++)
addfish(j, now);
for (int j = 1; j <= n; j++)
hs[0][pl[j]] = 0, hs[1][pl[j]] = 0;
ans = max(ans, sum);
}
printf("%d", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现