鱼贯而入(Miller Rabin)(Pollard Rho)(哈希)

鱼贯而入

题目大意

给你一些数,然后要你把它们放进一个哈希表里面。
要你选一个哈希表的大小,使得每个数在哈希表发现没有位置向后移动放置位置的次数尽可能的多。
输出这个最大的次数。

思路

首先我们考虑一个问题,假设我们枚举了哈希表的大小,如何用一种方法快速地求出次数。
如果长度小,我们可以用哈希暴力枚举搞。
如果大了,我们可以用另一个哈希把下表给离散。
然后就是哈希里面再套一个哈希。

然后你考虑要丢那些数进去算。
首先有一些数根本就不会发生重叠,那我们可以把这些数给排除掉。
那如果 x,y 在长度为 len 的哈希表中要有重叠,那它们模 len 余数要相同,就是 len| |xy|
那枚举每两个数之间的差,然后把它们的约数全部丢进来看一次。

但你发现你求约数个数太多,而且你求的过程就会炸。
考虑先处理约数个数太多的问题。
那你考虑 lenxlen,发现 len 一定会有 xlen 的寻址时间。
那就是说,如果这两个都可以,那 len 一定更优。
那我们就只需要找这些因子:它大于等于 n,但它除去它的最小质因子小于 n

那我们考虑有哪些满足,那大于等于 n 的质数一定可以,而且剩下有可能的一定会在 nn2 中。
为什么呢?那我们假设它除去它最小质因子尽可能小,那到最小就是它可以表示成 x2 形式,那它除去它的的最小质因子还是它的最小质因子,那这个数 x 被规定小于 n,那就得到 x<n
x2<n2,所以这个数不过超过 n2
那就知道要找那些数了,先是 nn2 的直接一股脑全部丢进去,反着才三万多,不多不多。
接着是讲每个 |aiaj| 质因数分解得出来大于 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; } //Miller Rabin 判断质数 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); } //Pollard Rho 算法 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 ;//小优化,因为我们只找不小于 n 的质因子 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() { // freopen("hash.in", "r", stdin); // freopen("hash.out", "w", stdout); 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++)//n~n*n 全部检验 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__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/jzoj_6468.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(72)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示