计蒜客 18487.Divisions-大数的所有因子个数-Miller_Rabin+Pollard_rho-超快的(大数质因解+因子个数求解公式) (German Collegiate Programming Contest 2015 ACM-ICPC Asia Training League 暑假第一阶段第三场 F)

这一场两个和大数有关的题目,都用到了米勒拉宾算法,有点东西,备忘一下。

题目传送门

 

 

F. Divisions

传送门

 

这个题是求一个数的所有因子个数,但是数据比较大,1e18,所以是大数的题目,正常的求因数的或者求质因数的都过不了,因为这一场的K是米勒拉宾判大素数,先过的K题,所以这个题直接头铁用Miller_Rabin+Pollard_rho这两个东西+因子个数求解公式写过去了。

 

这两个算法的具体原理不清楚。从别人那里知道了一点。

Miller_Rabin算法的作用是判断一个数是否是个素数,算法速度很快,虽然是概率算法,有一定误判概率,不过可以多次运算大幅度减少误判,误判概率与运算次数t有关,为2^(-t);当t够大时,误判的可能性就很小了。

Pollard_rho算法的作用是求一个数的因子,这个复杂度为O(sqrt(p)),p为这个数的因子。

参考来自别的的博客,传送门:算法集锦(特殊模板集)

 

因为Miller_Rabin+Pollard_rho这两个东西求出来的是一个数的所有质因数,所以最后要用因数个数求解公式来算出来结果。

关于因数个数求解公式:

对于任何一个自然数N,都可以分解质因子得到如下形式:

那么,N的因子的个数为:

N=100,分解质因子变形为:100=2252N的因子的个数为:f(N)=f(100)=(1+2)(1+2)=9。

即:1,2,4,5,10,20,25,50,100。

 

特判一下1就可以,找出来素因子之后,我是用map记了一下数然后用因子个数求解公式得到的结果。其他的没什么了。

 

代码:

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<iomanip>
  5 #include<stdio.h>
  6 #include<stdlib.h>
  7 #include<math.h>
  8 #include<cstdlib>
  9 #include<set>
 10 #include<map>
 11 #include<ctime>
 12 #include<stack>
 13 #include<queue>
 14 #include<vector>
 15 #include<set>
 16 using namespace std;
 17 typedef long long ll;
 18 const ll NUM=10;//运算次数,Miller_Rabin算法为概率运算,误判率为2^(-NUM);
 19 ll t,f[100];
 20 ll mul_mod(ll a,ll b,ll n)//求a*b%n,由于a和b太大,需要用进位乘法
 21 {
 22     a=a%n;
 23     b=b%n;
 24     ll s=0;
 25     while(b)
 26     {
 27         if(b&1)
 28             s=(s+a)%n;
 29         a=(a<<1)%n;
 30         b=b>>1;
 31     }
 32     return s;
 33 }
 34 ll pow_mod(ll a,ll b,ll n)//求a^b%n
 35 {
 36     a=a%n;
 37     ll s=1;
 38     while(b)
 39     {
 40         if(b&1)
 41             s=mul_mod(s,a,n);
 42         a=mul_mod(a,a,n);
 43         b=b>>1;
 44     }
 45     return s;
 46 }
 47 bool check(ll a,ll n,ll r,ll s)
 48 {
 49     ll ans=pow_mod(a,r,n);
 50     ll p=ans;
 51     for(ll i=1;i<=s;i++)
 52     {
 53         ans=mul_mod(ans,ans,n);
 54         if(ans==1&&p!=1&&p!=n-1)
 55             return true;
 56         p=ans;
 57     }
 58     if(ans!=1) return true;
 59     return false;
 60 }
 61 bool Miller_Rabin(ll n)//Miller_Rabin算法,判断n是否为素数
 62 {
 63     if(n<2) return false;
 64     if(n==2) return true;
 65     if(!(n&1)) return false;
 66     ll r=n-1,s=0;
 67     while(!(r&1)){r=r>>1;s++;}
 68     for(ll i=0;i<NUM;i++)
 69     {
 70         ll a=rand()%(n-1)+1;
 71         if(check(a,n,r,s))
 72             return false;
 73     }
 74     return true;
 75 }
 76 ll gcd(ll a,ll b)
 77 {
 78     return b==0?a:gcd(b,a%b);
 79 }
 80 ll Pollard_rho(ll n,ll c)//Pollard_rho算法,找出n的因子
 81 {
 82     ll i=1,j,k=2,d,p;
 83     ll x=rand()%n;
 84     ll y=x;
 85     while(true)
 86     {
 87         i++;
 88         x=(mul_mod(x,x,n)+c)%n;
 89         if(y==x) return n;
 90         if(y>x) p=y-x;
 91         else p=x-y;
 92         d=gcd(p,n);
 93         if(d!=1&&d!=n) return d;
 94         if(i==k)
 95         {
 96             y=x;
 97             k+=k;
 98         }
 99     }
100 }
101 void find(ll n)//找出n的所有因子
102 {
103     if(Miller_Rabin(n))
104     {
105         f[t++]=n;//保存所有因子
106         return;
107     }
108     ll p=n;
109     while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);//由于p必定为合数,所以通过多次求解必定能求得答案
110     find(p);
111     find(n/p);
112 }
113 int main()
114 {
115     srand(time(NULL));//随机数设定种子
116     ll n;cin>>n;
117     if(n==1){cout<<"1"<<endl;return 0;}
118     t=0;
119     find(n);
120     sort(f,f+t);
121     map<ll,int>q;
122     for(int i=0;i<t;i++)
123     {
124         q[f[i]]++;
125     }
126     map<ll,int>::iterator it;
127     ll ans=1;
128     for(it=q.begin();it!=q.end();it++)
129     {
130         int s=it->second;
131         ans*=1+s;
132     }
133     cout<<ans<<endl;
134     return 0;
135 }

 

 

 

 

posted @ 2018-07-12 22:10  ZERO-  阅读(969)  评论(0编辑  收藏  举报