German Collegiate Programming Contest 2015 ACM-ICPC Asia Training League 暑假第一阶段第三场 F. Divisions-大数的所有因子个数-Miller_Rabin+Pollard_rho(大数质因解+因子个数求解公式) G. Extreme Sort K. Upside down primes-米勒拉宾判大素数

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

题目传送门

F. Divisions

传送门

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

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

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

扫描二维码关注公众号,回复: 2145051 查看本文章

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 }

 E. Change of Scenery

传送门

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn=1e5+10;
 8 int a[maxn];
 9 int main(){
10     int n;
11     scanf("%d",&n);
12     for(int i=0;i<n;i++)
13         scanf("%d",&a[i]);
14     for(int i=1;i<n;i++){
15         if(a[i]<a[i-1]){
16             cout<<"no"<<endl;
17             return 0;
18         }
19     }
20     cout<<"yes"<<endl;
21 }
View Code

 K. Upside down primes

传送门

这个题就是把大数按字符串输进去,判断一下是不是素数,然后反转180度,先判断反转之后的东西是不是一个数,如果是的话,再把这个数判一下是不是素数,如果都满足条件就yes。

直接调用两次米勒拉宾判大素数就可以了。

代码:

  1 //K-米勒拉宾判大素数
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<iomanip>
  6 #include<stdio.h>
  7 #include<stdlib.h>
  8 #include<math.h>
  9 #include<cstdlib>
 10 #include<set>
 11 #include<map>
 12 #include<ctime>
 13 #include<stack>
 14 #include<queue>
 15 #include<vector>
 16 #include<set>
 17 using namespace std;
 18 typedef long long ll;
 19 #define INF 0x7fffffff
 20 #define LIT 0x3f3f3f3f
 21 #define me(a,b) memset(a,b,sizeof(a))
 22 #define PI acos(-1.0)
 23 #define ios ios::sync_with_stdio(0),cin.tie(0);
 24 const int N=1e5+5;
 25 const int maxn=1e9;
 26 const int S=20;
 27 ll sw(ll a)
 28 {
 29     if(a==0||a==2||a==5||a==8||a==1)
 30         return a;
 31     if(a==6)
 32         return 9;
 33     if(a==9)
 34         return 6;
 35     else
 36         return -1;
 37 }
 38 ll mult_mod(ll a,ll b,ll mod)
 39 {
 40     a%=mod;b%=mod;
 41     ll ans=0;
 42     while(b)
 43     {
 44         if(b&1)
 45         {
 46             ans=ans+a;
 47             if(ans>=mod)
 48             ans=ans-mod;
 49         }
 50         a=a<<1;
 51         if(a>=mod) a=a-mod;
 52         b=b>>1;
 53     }
 54     return ans;
 55 }
 56 ll pow_mod(ll a,ll b,ll mod)
 57 {
 58     ll ans=1;
 59     a=a%mod;
 60     while(b)
 61     {
 62         if(b&1)
 63         {
 64             ans=mult_mod(ans,a,mod);
 65         }
 66         a=mult_mod(a,a,mod);
 67         b=b>>1;
 68     }
 69     return ans;
 70 }
 71 bool check(ll a,ll n,ll x,ll t)
 72 {
 73     ll ret=pow_mod(a,x,n);
 74     ll last=ret;
 75     for(int i=1;i<=t;i++)
 76     {
 77         ret=mult_mod(ret,ret,n);
 78         if(ret==1&&last!=1&&last!=n-1)
 79             return true;
 80         last=ret;
 81     }
 82     if(ret!=1) return true;
 83     else return false;
 84 }
 85 bool Miller_Rabin(ll n)
 86 {
 87     if(n<2)return false;
 88     if(n==2) return true;
 89     if((n&1)==0) return false;
 90     ll x=n-1,t=0;
 91     while((x&1)==0){x>>=1;t++;}
 92     for(int i=0;i<S;i++)
 93     {
 94         ll a=rand()%(n-1)+1;
 95         if(check(a,n,x,t))
 96             return false;
 97     }
 98     return true;
 99 }
100 char ch[20];
101 int main()
102 {
103     cin>>ch;
104     ll n=0,bit=1;
105     int len=strlen(ch);
106     bool flag=false;
107     for(int i=len-1;i>=0;i--)
108     {
109         n+=(ch[i]-'0')*bit;
110         bit*=10;
111     }
112     if(!Miller_Rabin(n))
113     {
114         cout<<"no"<<endl;
115         return 0;
116     }
117     n=0;bit=1;
118     for(int i=0;i<len;i++){
119         ll cnt=sw(ch[i]-'0');
120         if(cnt==-1){
121             cout<<"no"<<endl;
122             return 0;
123         }
124         n+=cnt*bit;
125         bit*=10;
126     }
127     if(Miller_Rabin(n))
128         cout<<"yes"<<endl;
129     else
130         cout<<"no"<<endl;
131 }
View Code

就这些,没了。

因为这场没找到数据结构的题,就和一个队友一起刚了数论题,这场主要是图论和数论。难过,图论选手不理我,是真的回家休息去了,上一场比赛的A题和这场的图论题都比较好,如果,你能看到的话,可以去看看这些题,正好和你最近学的算法有关系。

唉,算了,都讨厌死我了。

就这些,备忘一下,米勒拉宾有点厉害。

最近在慢慢改代码风格,因为我发现队友们写代码都是把大括号对齐了写,现在再改回来。

继续去爱我的线段树去了,我滚了。

对于任何一个自然数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 }

 E. Change of Scenery

传送门

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int maxn=1e5+10;
 8 int a[maxn];
 9 int main(){
10     int n;
11     scanf("%d",&n);
12     for(int i=0;i<n;i++)
13         scanf("%d",&a[i]);
14     for(int i=1;i<n;i++){
15         if(a[i]<a[i-1]){
16             cout<<"no"<<endl;
17             return 0;
18         }
19     }
20     cout<<"yes"<<endl;
21 }
View Code

 K. Upside down primes

传送门

这个题就是把大数按字符串输进去,判断一下是不是素数,然后反转180度,先判断反转之后的东西是不是一个数,如果是的话,再把这个数判一下是不是素数,如果都满足条件就yes。

直接调用两次米勒拉宾判大素数就可以了。

代码:

  1 //K-米勒拉宾判大素数
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<iomanip>
  6 #include<stdio.h>
  7 #include<stdlib.h>
  8 #include<math.h>
  9 #include<cstdlib>
 10 #include<set>
 11 #include<map>
 12 #include<ctime>
 13 #include<stack>
 14 #include<queue>
 15 #include<vector>
 16 #include<set>
 17 using namespace std;
 18 typedef long long ll;
 19 #define INF 0x7fffffff
 20 #define LIT 0x3f3f3f3f
 21 #define me(a,b) memset(a,b,sizeof(a))
 22 #define PI acos(-1.0)
 23 #define ios ios::sync_with_stdio(0),cin.tie(0);
 24 const int N=1e5+5;
 25 const int maxn=1e9;
 26 const int S=20;
 27 ll sw(ll a)
 28 {
 29     if(a==0||a==2||a==5||a==8||a==1)
 30         return a;
 31     if(a==6)
 32         return 9;
 33     if(a==9)
 34         return 6;
 35     else
 36         return -1;
 37 }
 38 ll mult_mod(ll a,ll b,ll mod)
 39 {
 40     a%=mod;b%=mod;
 41     ll ans=0;
 42     while(b)
 43     {
 44         if(b&1)
 45         {
 46             ans=ans+a;
 47             if(ans>=mod)
 48             ans=ans-mod;
 49         }
 50         a=a<<1;
 51         if(a>=mod) a=a-mod;
 52         b=b>>1;
 53     }
 54     return ans;
 55 }
 56 ll pow_mod(ll a,ll b,ll mod)
 57 {
 58     ll ans=1;
 59     a=a%mod;
 60     while(b)
 61     {
 62         if(b&1)
 63         {
 64             ans=mult_mod(ans,a,mod);
 65         }
 66         a=mult_mod(a,a,mod);
 67         b=b>>1;
 68     }
 69     return ans;
 70 }
 71 bool check(ll a,ll n,ll x,ll t)
 72 {
 73     ll ret=pow_mod(a,x,n);
 74     ll last=ret;
 75     for(int i=1;i<=t;i++)
 76     {
 77         ret=mult_mod(ret,ret,n);
 78         if(ret==1&&last!=1&&last!=n-1)
 79             return true;
 80         last=ret;
 81     }
 82     if(ret!=1) return true;
 83     else return false;
 84 }
 85 bool Miller_Rabin(ll n)
 86 {
 87     if(n<2)return false;
 88     if(n==2) return true;
 89     if((n&1)==0) return false;
 90     ll x=n-1,t=0;
 91     while((x&1)==0){x>>=1;t++;}
 92     for(int i=0;i<S;i++)
 93     {
 94         ll a=rand()%(n-1)+1;
 95         if(check(a,n,x,t))
 96             return false;
 97     }
 98     return true;
 99 }
100 char ch[20];
101 int main()
102 {
103     cin>>ch;
104     ll n=0,bit=1;
105     int len=strlen(ch);
106     bool flag=false;
107     for(int i=len-1;i>=0;i--)
108     {
109         n+=(ch[i]-'0')*bit;
110         bit*=10;
111     }
112     if(!Miller_Rabin(n))
113     {
114         cout<<"no"<<endl;
115         return 0;
116     }
117     n=0;bit=1;
118     for(int i=0;i<len;i++){
119         ll cnt=sw(ch[i]-'0');
120         if(cnt==-1){
121             cout<<"no"<<endl;
122             return 0;
123         }
124         n+=cnt*bit;
125         bit*=10;
126     }
127     if(Miller_Rabin(n))
128         cout<<"yes"<<endl;
129     else
130         cout<<"no"<<endl;
131 }
View Code

就这些,没了。

因为这场没找到数据结构的题,就和一个队友一起刚了数论题,这场主要是图论和数论。难过,图论选手不理我,是真的回家休息去了,上一场比赛的A题和这场的图论题都比较好,如果,你能看到的话,可以去看看这些题,正好和你最近学的算法有关系。

唉,算了,都讨厌死我了。

就这些,备忘一下,米勒拉宾有点厉害。

最近在慢慢改代码风格,因为我发现队友们写代码都是把大括号对齐了写,现在再改回来。

继续去爱我的线段树去了,我滚了。

猜你喜欢

转载自www.cnblogs.com/ZERO-/p/9302169.html