1. 程式人生 > 其它 >Miller-Rabin + Pollard-rho

Miller-Rabin + Pollard-rho

Miller-Rabin + Pollard-Rho演算法模板題2019 ACM-ICPC Asia Xuzhou E

題意:給一個大小為\(n\)的序列a,定義$ Z = \Pi_{i=1}^{n} a_i$。另給出 \(X ,Y\),若\(b_i = Z * X^{i}\),求最大的\(i\)使得\(b_i | Y!\)

題解:板子題,題解略。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(b)-1;i>=(a);i--)
#define pb push_back
#define db double
#define VI vector<int>
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<int,int>
#define fi first
#define se second
mt19937_64 mrand(random_device{}());
ll rnd(ll x){return mrand()%x;}
#define all(x) x.begin(),x.end()
ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
const ll mod = 1e9+7;
ll qpow(ll a,ll b){ll t=1;assert(b>=0);for(;b;b>>=1){if(b&1)t=t*a%mod;a=a*a%mod;}return t;}

//時間複雜度7*log^3(n)
ll qmul(ll a,ll b,ll mod)//快速乘
{
    ll c=(ld)a/mod*b;
    ll res=(ull)a*b-(ull)c*mod;
    return (res+mod)%mod;
}
ll qpow(ll a,ll n,ll mod)//快速冪
{
    ll res=1;
    while(n)
    {
        if(n&1) res=qmul(res,a,mod);
        a=qmul(a,a,mod);
        n>>=1;
    }
    return res;
}
bool MRtest(ll n)//Miller Rabin Test
{
    if(n<3||n%2==0) return n==2;//特判
    ll u=n-1,t=0;
    while(u%2==0) u/=2,++t;
    //ll ud[] = {2,7,61};
    ll ud[]={2,325,9375,28178,450775,9780504,1795265022};
    for(ll a:ud)
    {
        ll v=qpow(a,u,n);
        if(v==1||v==n-1||v==0) continue;
        for(int j=1;j<=t;j++)
        {
            v=qmul(v,v,n);
            if(v==n-1&&j!=t){v=1;break;}//出現一個n-1,後面都是1,直接跳出
            if(v==1) return 0;//這裡代表前面沒有出現n-1這個解,二次檢驗失敗
        }
        if(v!=1) return 0;//Fermat檢驗
    }
    return 1;
}

//找到n的一個因子,時間複雜度是O(n^(1/4))
//結合Miller-Rabin可用來求出最大素因子
//模板題:https://www.luogu.com.cn/problem/P4718
//進一步優化可以考慮倍增和固定上限結合
ll Pollard_Rho(ll N){
  if (N == 4)return 2;
  if (MRtest(N))return N;
  while(1){
    ll c = rnd(N-1) + 1;
    auto f = [=](ll x) { return ((__int128)x * x + c) % N; };
    ll t = 0, r = 0, p = 1, q;
    do{
      for (int i = 0; i < 128; ++i){ // 令固定距離C=128
        t = f(t), r = f(f(r));
        if (t == r || (q = (__int128)p * abs(t - r) % N) == 0) // 如果發現環,或者積即將為0,退出
          break;
        p = q;
      }
      ll d = gcd(p, N);
      if (d > 1)return d;
    }while (t != r);
  }
}

ll max_prime_factor(ll x){
  ll fac = Pollard_Rho(x);
  if (fac == x)return x;
  else return max(max_prime_factor(fac), max_prime_factor(x / fac));
}

const int N = 1e5+5;
ll a[N],x,y;
int n;
ll fac[30],idx;
int fac_cnt[30];

void chai(ll x){
  while(x!=1){
    ll temp = max_prime_factor(x);
    fac[++idx] = temp;
    while(x%temp==0){
      fac_cnt[idx]++;
      x/=temp;
    }
  }
}

ll cal(ll n,ll p){
  ll res = 0;
  while(n){
    res += n/p;
    n/=p;
  }
  return res;
}

ll solve(){
  ll ans = 4e18;
  for(int i=1;i<=idx;i++){
    ll& p = fac[i];
    ll ty = cal(y,p);
    for(int j=1;j<=n;j++){
      ty -= cal(a[j],p);
    }
    ans = min(ans,ty/fac_cnt[i]);
  }
  return ans;
}

int main(){
  int t;cin >> t;
  while(t--){
    idx = 0;
    memset(fac_cnt,0,sizeof fac_cnt);
    scanf("%d%lld%lld",&n,&x,&y);
    for(int i=1;i<=n;i++)scanf("%lld",a+i); 
    chai(x);
    ll ans = solve();
    cout << ans << endl;
  }
}