poj 1845 【数论:逆元,二分(乘法),拓展欧几里得,费马小定理】

POJ 1845

题意不说了,网上一大堆。此题做了一天,必须要整理一下了。

刚开始用费马小定理做,WA。(poj敢说我代码WA???)(以下代码其实都不严谨,按照数据要求A是可以等于0的,那么结果自然就是0了,需要特判一下,但是poj好像没有为0的数据,能AC。先不改了。)

后来看了好多人的博客,发现很少用费马小定理写的,或者写的代码我看不下去。。就先用那个什么二分等比数列写了一下。

过程也不说了,很多博客都说了。(【1】【2】):

 

 1 #include<iostream>
 2 #include<cmath>
 3 #define mod 9901
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=1000;
 7 ll p[maxn],r[maxn];
 8 int tot;
 9 
10 void Factor(ll n)//质因数分解
11 {
12     tot=0;
13     int m=sqrt(n+0.5);
14     for (int i=2;i<=m;i++)
15     if (n%i==0){
16         p[++tot]=i;
17         r[tot]=0;
18         while(n%i==0){
19             ++r[tot];
20             n/=i;
21         }
22     }
23     if(n>1){
24         p[++tot]=n;
25         r[tot]=1;
26     }
27 }
28 
29 ll qpow(ll a,ll b)//快速幂
30 {
31     ll r=1;
32     while(b)
33     {
34         if(b&1) r=r*a%mod;
35         b>>=1;
36         a=a*a%mod;
37     }
38     return r;
39 }
40 
41 
42 ll sum(int p,int n)//二分等比数列
43 {
44     if(n==0)
45         return 1;
46     if(n&1)
47         return (sum(p,n/2)%mod*(1+qpow(p,n/2+1))%mod)%mod;
48     else
49         return (sum(p,n/2-1)%mod*(1+qpow(p,n/2+1))%mod+qpow(p,n/2))%mod;
50 }
51 
52 int main()
53 {
54     ll A,B;
55     cin>>A>>B;
56     Factor(A);
57     ll ans=1;
58     for (int i=1;i<=tot;i++)
59     {
60         ans=(ans%mod*sum(p[i],r[i]*B)%mod)%mod;
61     }
62     cout<<ans<<endl;
63     return 0;
64 }

 

 

 

后来,又看见有人用二分乘法写:

 

 1 #include<iostream>
 2 #include<cmath>
 3 #define mod 9901
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn = 1000;
 7 ll p[maxn], r[maxn];
 8 int tot;
 9 
10 void Factor(ll n)
11 {
12     tot = 0;
13     int m = sqrt(n + 0.5);
14     for (int i = 2; i <= m; i++)
15         if (n%i == 0) {
16             p[++tot] = i;
17             r[tot] = 0;
18             while (n%i == 0) {
19                 ++r[tot];
20                 n /= i;
21             }
22         }
23     if (n>1) {
24         p[++tot] = n;
25         r[tot] = 1;
26     }
27 }
28 
29 
30 ll muti(ll a, ll b, ll m)//二分乘法???求教!!!
31 {
32     ll ans = 0;
33     a %= m;
34     while (b)
35     {
36         if (b & 1)
37         {
38             ans = (ans + a) % m;
39             b--;
40         }
41         b >>= 1;
42         a = (a + a) % m;
43     }
44     return ans;
45 }
46 
47 ll qpow(ll a, ll b, ll m)
48 {
49     ll ans = 1;
50     a %= m;
51     while (b)
52     {
53         if (b & 1) {
54             ans = muti(ans, a, m);
55             b--;
56         }
57         b >>= 1;
58         a = muti(a, a, m);
59     }
60     return ans;
61 }
62 
63 
64 int main()
65 {
66     ll A, B;
67     cin >> A >> B;
68 
69     Factor(A);
70     ll ans = 1;
71     for (int i = 1; i <= tot; i++)
72     {
73         ll m = (p[i] - 1)*mod;
74         ans *= (qpow(p[i], r[i] * B + 1, m) - 1 + m) / (p[i] - 1);
75         ans %= mod;
76     }
77     cout << ans << endl;
78     return 0;
79 }

 

 

 

说实话我现在还不懂为什么要二分乘法?啥子是二分乘法?这是ACdreamer(【1】)(应该是个大牛吧!!!)里面写的,后来也到过其他博客写过,代码都极其相似。ACdreamer说mb会可能会超int范围,所以快速幂时二分乘法。???我现在也很懵逼,mb超int怎么了?不超long long不就行了?难道超int模会出错?求解释啊!!!

后来,又看到了有人用拓展欧几里得:

 

 1 #include<iostream>
 2 #include<cmath>
 3 #define mod 9901
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=1000;
 7 ll p[maxn],r[maxn];
 8 int tot;
 9 
10 void Factor(ll n)
11 {
12     tot=0;
13     int m=sqrt(n+0.5);
14     for (int i=2;i<=m;i++)
15     if (n%i==0){
16         p[++tot]=i;
17         r[tot]=0;
18         while(n%i==0){
19             ++r[tot];
20             n/=i;
21         }
22     }
23     if(n>1){
24         p[++tot]=n;
25         r[tot]=1;
26     }
27 }
28 
29 ll exgcd(ll a,ll b,ll &x,ll &y)
30 {
31     if(b==0){
32         x=1,y=0;
33         return a;
34     }
35     int ret=exgcd(b,a%b,x,y);
36     int t=x;
37     x=y;
38     y=t-a/b*y;
39     return ret;
40 }
41 
42 ll inv(ll n)
43 {
44     n%=mod;
45     ll a=n,b=mod,x,y;
46     exgcd(a,b,x,y);
47     x=(x%mod+mod)%mod;
48     return x;
49 }
50 
51 ll qpow(ll a,ll b)
52 {
53     ll r=1;
54     while(b)
55     {
56         if(b&1) r=a*r%mod;
57         b>>=1;
58         a=a*a%mod;
59     }
60     return r;
61 }
62 
63 int main()
64 {
65     ll A,B;
66     cin>>A>>B;
67 
68     Factor(A);
69     ll ans=1;
70     for (int i=1;i<=tot;i++)
71     {
72         if(p[i]%mod==1){//!!!
73             ans*=(r[i]*B+1)%mod;
74             ans%=mod;
75         }else{
76             ans*=((qpow(p[i],r[i]*B+1)-1+mod)%mod*inv(p[i]-1))%mod;
77             ans%=mod;
78         }
79     }
80     cout<<ans<<endl;
81     return 0;
82 }

 

能拓展欧几里得那么费马小定理也应该差不多啊!!!

所以回头我又写了费马小定理的:

 1 #include<iostream>
 2 #include<cmath>
 3 #define mod 9901
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn=1000;
 7 ll p[maxn],r[maxn];
 8 int tot;
 9 
10 void Factor(ll n)
11 {
12     tot=0;
13     int m=sqrt(n+0.5);
14     for (int i=2;i<=m;i++)
15     if (n%i==0){
16         p[++tot]=i;
17         r[tot]=0;
18         while(n%i==0){
19             ++r[tot];
20             n/=i;
21         }
22     }
23     if(n>1){
24         p[++tot]=n;
25         r[tot]=1;
26     }
27 }
28 
29 ll qpow(ll a,ll b)
30 {
31     ll r=1;
32     a%=mod;
33     while(b)
34     {
35         if(b&1) r=a*r%mod;
36         b>>=1;
37         a=a*a%mod;
38     }
39     return r;
40 }
41 
42 int main()
43 {
44     ll A,B;
45     cin>>A>>B;
46 
47     Factor(A);
48     ll ans=1;
49     for (int i=1;i<=tot;i++)
50     {
51         
52         if(p[i]%mod==1){//!!!
53             ans*=(r[i]*B+1)%mod;
54             ans%=mod;
55         }else{
56             ans*=((qpow(p[i],r[i]*B+1)-1+mod)%mod*qpow(p[i]-1,mod-2)%mod)%mod;//直接用费马小定理求逆元,qpow(p[i]-1,mod-2);
57             ans%=mod;
58         }
59     }
60     cout<<ans<<endl;
61     return 0;
62 }

直到这时,我才明白我以前写费马为什么错了。因为当素因子p[i]%mod==1时,求逆元的结果是错的,或者就像有的博客说这时是求不出逆元的。因为这时qpow(p[i]-1,mod-2)的结果一定为0。因为当求(1+p^1+p^2+p^3...+p^r)%mod时,由于p%mod==1,所以每项%mod都是1,结果就是r+1。也就是上面两个代码特判的结果:r[i]*B+1。

最后提供一个数据: 59407 3

我的结果是:4

没有特判的话,结果会是0。这个就是我找到的第一个大于9901且%9901=1的质数。

感谢网上的广大博客,终于让我看了一天后悟出了这一点。。。。。

本来想巩固一下逆元的概念,结果ACdreamer里面看到的这道题弄了一天。。。

希望以后能看到这个文章的朋友们能够给我解释一下为什么要二分乘法?不理解啊!先谢谢!!

 

谢谢这些博客们,有可能没有一一列出,你们至多至少都给了我一点启发和帮助!也希望你们写博客的时候在关键能多说几句>_<.

 

 【1】http://blog.csdn.net/acdreamers/article/details/8220787

【2】http://blog.csdn.net/lyy289065406/article/details/6648539

【3】http://blog.csdn.net/a601025382s/article/details/12233213

【4】http://blog.csdn.net/hlmfjkqaz/article/details/9735143

【5】http://www.cnblogs.com/mjy0724/p/4371752.html

【6】http://m.blog.csdn.net/kbdwo/article/details/9798193

【7】http://www.cnblogs.com/linyujun/p/5194184.html

 

posted @ 2017-08-03 17:12  ╰追憶似水年華ぃ╮  阅读(183)  评论(0编辑  收藏  举报