【省选模拟】鱼贯而入(Pollard-Rho)

H e r e Here

题意:寻找一个模数使得一组数的 h a s h hash 寻址时间最长, n 200 , A i 1 e 18 n\le 200,A_i\le 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 然后逐一计算比较
    一个显而易见的条件是 i , j , s . t   l e n a i a j \exists i,j, s.t\ len|a_{i}-a_j ,否则寻址时间一定为 0
    于是就有了我们的做法,枚举每一对 i , j i,j ,找到所有的约数,再进行判断,判断可以用并查集做到近似 O ( n ) O(n) ,那么我们的复杂度就是 n 3 A 1 / 3 + n 2 A 1 / 2 n^3*A^{1/3}+n^2*A^{1/2} 后面的为分解复杂度
    考虑 A 1 e 18 A\le 1e18 的时候,分解可以用 p o l l a r d r h o pollard-rho ,需要优化前面的复杂度
    发现如果一个数 p n p\ge n 可行,那么 k p kp 均没有 p p
    于是我们需要判断的仅有 i n , i / m i n p ( i ) n i\ge n,i/minp(i)\le n 的哪些 i i
    这些数分为两类,一类是 [ n , n 2 ] \in[n,n^2] 的合数及质数,一类是 > n 2 > n^2 的质数
    第二个用 p o l l a r d r h o pollard-rho 分解出来,然后把 [ n , n 2 ] \in[n,n^2] 的所有数 c h e c k check 一遍
    这样子复杂度是 O ( n ( n 2 + l o g A ) + n 2 A 1 / 4 ) O(n*(n^2+logA)+n^2A^{1/4})
    然后并不用并查集,严重卡不满, h a s h 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;
}
发布了634 篇原创文章 · 获赞 98 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/104190543