题意:寻找一个模数使得一组数的 h a s h hash hash 寻址时间最长, n ≤ 200 , A i ≤ 1 e 18 n\le 200,A_i\le 1e18 n≤200,Ai≤1e18,其中寻址代码如下
void add_fish(long long &cnt, long long x, long long len) {
long long y = x % len;
while(h[y] != -1 && h[y] != x)
y = (y + 1) % len, cnt ++;
h[y] = x;
}
long long solve(long long len) {
for(int i = 0; i < len; i ++) h[i] = -1;
long long cnt = 0;
for(int i = 1; i <= n; i ++) add_fish(cnt, a[i], len);
return cnt;
}
- 题解
直接算显然没法算,考虑找几个较优的 l e n len len 然后逐一计算比较
一个显而易见的条件是 ∃ i , j , s . t l e n ∣ a i − a j \exists i,j, s.t\ len|a_{i}-a_j ∃i,j,s.t len∣ai−aj,否则寻址时间一定为 0
于是就有了我们的做法,枚举每一对 i , j i,j i,j,找到所有的约数,再进行判断,判断可以用并查集做到近似 O ( n ) O(n) O(n),那么我们的复杂度就是 n 3 ∗ A 1 / 3 + n 2 ∗ A 1 / 2 n^3*A^{1/3}+n^2*A^{1/2} n3∗A1/3+n2∗A1/2 后面的为分解复杂度
考虑 A ≤ 1 e 18 A\le 1e18 A≤1e18 的时候,分解可以用 p o l l a r d − r h o pollard-rho pollard−rho,需要优化前面的复杂度
发现如果一个数 p ≥ n p\ge n p≥n 可行,那么 k p kp kp 均没有 p p p 优
于是我们需要判断的仅有 i ≥ n , i / m i n p ( i ) ≤ n i\ge n,i/minp(i)\le n i≥n,i/minp(i)≤n 的哪些 i i i
这些数分为两类,一类是 ∈ [ n , n 2 ] \in[n,n^2] ∈[n,n2] 的合数及质数,一类是 > n 2 > n^2 >n2 的质数
第二个用 p o l l a r d − r h o pollard-rho pollard−rho 分解出来,然后把 ∈ [ n , n 2 ] \in[n,n^2] ∈[n,n2] 的所有数 c h e c k check check 一遍
这样子复杂度是 O ( n ∗ ( n 2 + l o g A ) + n 2 A 1 / 4 ) O(n*(n^2+logA)+n^2A^{1/4}) O(n∗(n2+logA)+n2A1/4)
然后并不用并查集,严重卡不满, h a s h hash hash 模拟即可
先弱化条件,找到一群较优的来判断比较巧妙
#include<bits/stdc++.h>
#define cs const
using namespace std;
typedef long long ll;
ll read(){
ll cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int N = 205, K = 1e6 + 50;
int n; ll a[N];
int prim[K], minp[K], pc; bool isp[K];
vector<ll> fac, S;
ll rnd(){ return rand()|((ll)rand()<<15)|((ll)rand()<<30)|((ll)rand()<<45); }
ll mul(ll x, ll y, ll mod){ return (ll)(x * y - (ll)((long double)x / mod * y) * mod + mod) % mod; }
ll ksm(ll a, ll b, ll mod){ ll ans = 1; for(;b;b>>=1,a=mul(a,a,mod)) if(b&1) ans=mul(ans,a,mod); return ans; }
ll gcd(ll a, ll b){ return !b ? a : gcd(b, a % b); }
void linear_sieve(int n){
for(int i = 2; i <= n; i++){
if(!isp[i]) minp[i] = i, prim[++pc] = i;
for(int j = 1; j <= pc; j++){
if(i * prim[j] > n) break;
isp[i * prim[j]] = true;
minp[i * prim[j]] = prim[j];
if(i % prim[j] == 0) break;
}
}
}
bool miller(ll x){
if(x <= K - 50) return !isp[x];
static int c[17] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,51,53};
ll t = x-1, y = 0;
while(t&1^1) t>>=1, ++y;
for(int i = 0; i < 5; i++){
ll p = c[rand() % 17], now = ksm(p, t, x);
for(int j = 1; j <= y; j++){
ll nxt = mul(now, now, x);
if(nxt == 1 && now != 1 && now != x-1) return false;
now = nxt;
} if(now != 1) return false;
} return true;
}
ll Rho(ll x){
ll c = (ll)rnd() % (x-1) + 1, nxt = 1, now = 0;
for(int k = 2; ; k <<= 1){
ll prod = 1;
for(int i = 1; i <= k; i++){
nxt = mul(nxt, nxt, x) + c; if(nxt >= x) nxt -= x;
prod = mul(prod, abs(nxt-now), x);
}
ll g = gcd(prod, x); if(g > 1) return g;
now = nxt;
}
}
void Factor(ll x){
if(x == 1) return;
if(miller(x)){ fac.push_back(x); return; }
ll now = Rho(x);
while(x % now == 0) x /= now;
Factor(now); Factor(x);
}
void Solve(ll x){
for(int i = 1; i <= 200; i++) if(x % prim[i] == 0){
fac.push_back(prim[i]);
while(x % prim[i] == 0) x /= prim[i];
} Factor(x);
}
int ans, sta[N], top;
cs int P = 19260817;
ll key[P+1], vl[P+1];
int locate(ll x){
int ps = x % P;
while(key[ps] != -1 && key[ps] != x) ps = ps == P-1 ? 0 : ps+1;
return ps;
}
void calc(ll len){
int ct = 0;
for(int i = 1; i <= n; i++){
for(ll x = a[i] % len; ; x = x == len-1 ? 0 : x+1, ++ct){
int p = locate(x);
if(key[p] == -1){
sta[++top] = p;
key[p] = x; vl[p] = a[i];
break;
} if(vl[p] == a[i]) break;
}
}
while(top) key[sta[top--]] = -1;
ans = max(ans, ct);
}
int main(){
srand(time(0));
linear_sieve(K - 50);
read();
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++) if(a[i] ^ a[j]){
ll x = a[i] - a[j]; if(x < 0) x = -x;
fac.clear(); Solve(x);
for(ll x : fac) if(x >= n * n) S.push_back(x);
}
memset(key, -1, sizeof(key));
for(int i = n; i <= n * n; i++) if(i / minp[i] < n) calc(i);
for(ll x : S) calc(x);
cout << ans;
return 0;
}