数论

一、同余定理

同余式 : a ≡ b (mod m) (即 a%m == b%m)

简单粗暴的说就是:若 a-b == m 那么 a%m == b%m

这个模运算性质一眼看出。。。直接上入门水题:

Reduced ID Numbers

附AC代码(这个也没啥模板。。。。知道就好)

[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<stdlib.h>  
  5. #define mem(a,x) memset(a,x,sizeof(a))  
  6. using namespace std;  
  7. typedef long long ll;  
  8. /* 
  9. 同余: 
  10. if 
  11.     a-b == m 
  12. then 
  13.     a%m == b%m 
  14.  
  15. */  
  16. const int N = 1000000;  
  17. bool vis[N+5];  
  18. int a[N+5];  
  19. bool ol[N+5];  
  20. int main()  
  21. {  
  22.     int T;cin>>T;  
  23.     while (T--)  
  24.     {  
  25.         int n;  
  26.         scanf("%d",&n);  
  27.         mem(vis,0);  
  28.         for (int i = 1;i <= n;++i) scanf("%d",a+i);  
  29.         for (int i = 1;i <= n;++i)  
  30.         {  
  31.             for (int j = i+1;j <= n;++j)  
  32.             {  
  33.                 int d = abs(a[i]-a[j]);  
  34.                 vis[d] = 1;  
  35.             }  
  36.         }  
  37.         bool fd = 0;  
  38.         for (int m = 1;!fd;++m)  
  39.         {  
  40.             if (!vis[m])  
  41.             {  
  42.                 mem(ol,0);bool ok = 1;  
  43.                 for (int i = 1;ok&&i <= n;++i)  
  44.                 {  
  45.                     if (ol[a[i]%m]) ok = 0;  
  46.                     else ol[a[i]%m] = 1;  
  47.                 }  
  48.                 if (ok)  
  49.                 {  
  50.                     printf("%d\n",m);  
  51.                     fd = 1;  
  52.                 }  
  53.             }  
  54.         }  
  55.     }  
  56.     return 0;  
  57. }  

二、扩展欧几里德算法

这个扩展是从原欧几里德算法扩展而来,这个算法真心非常有用!非常有用!非常有用!(中国剩余定理也要用到它)

首先说欧几里德算法(其实就是我们小时候数学课上就学过的辗转相除法求gcd)

欧几里德说:gcd(a,b) = gcd(b,a%b)

于是得到欧几里德算法:

[cpp]  view plain  copy
  1. int gcd(int a,int b)  
  2. {  
  3.     return b==0?a:gcd(b,a%b);  
  4. }  
该算法可以在几乎是log的时间算a,b的最大公因数gcd

PS:__gcd(a,b)库函数可以直接调用,但是有些OJ上提交会CE

现在,我们令a,b的最大公因数为gcd,那么我们一定可以找到一组x,y满足:(这个是欧几里德的定理一)

[cpp]  view plain  copy
  1. a*x + b*y = gcd;  

此时,我们设x0,y0是上述不定方程的特解,那么就可以用x0,y0表示出整个不定方程的通解

[cpp]  view plain  copy
  1. x = x0 + (b/gcd)*t;  
  2. y = y0 - (a/gcd)*t;  

然后是怎么求解通解x,y?

倒过去看看欧几里德算法,显然,它的结束条件是 b = 0的时候返回a

这就意味着,终止状态是 :a = gcd ,b = 0;

将这组a,b代会不定方程ax+by=gcd,可以得到一组特解:x = 1,y = 0

找到终止状态,我们再看看递归的过程:

[cpp]  view plain  copy
  1. gcd = b*x1 + (a%b)*y1   
  2.     = b*x1 + (a-(a/b)*b)*y1 // a%b = a-(a/b)*b  
  3.     = b*x1 + a*y1 - (a/b)*b*y1  
  4.     = a*y1 + b*(x1-a/b*y1)  

最后得到gcd = a*y1 + b*(x1-a/b*y1)和原来的式子gcd = a*x + b*y一对比就得到:

[cpp]  view plain  copy
  1. x = y1,y = x1 - a/b*y1;  

上面两个等式很重要!!(因为模板里面会直接用到,如果是直接背模板就得把它背熟)

下面就是扩展欧几里德算法:

[cpp]  view plain  copy
  1. int e_gcd(int a,int b,int &x,int &y)  
  2. {  
  3.     if (b == 0)  
  4.     {  
  5.         x = 1,y = 0;  
  6.         return a;  
  7.     }  
  8.     int ans = e_gcd(b,a%b,x,y);  
  9.     int tmp = x;  
  10.     x = y;  
  11.     y = tmp - a/b*y;  
  12. }  

这个算法本质上还是求a,b的最大公因数,只是在计算的过程中顺带计算x,y(同时体验了一把引用的神奇)

扩展欧几里德算法有什么用?反正用法很多,它可以求解形如 ax+by=c的通解

但是实际应用中通常指要求在通解中选一些特殊的解,

比如一个数对于另一个数的乘法逆元。那么什么是乘法逆元?

[cpp]  view plain  copy
  1. ax ≡ 1(mod b) // ax % b = 1  
这里,我们称x是a关于b的乘法逆元

ax%b = 1,可以化成 ax = by + 1

显然,它等价于这样的表达式:ax + by = 1             

这个式子很眼熟有木有!!!如果等式右边是gcd(a,b)就好了!!!

然后这里copy欧几里德的三个定理:

[cpp]  view plain  copy
  1. 定理一:如果d = gcd(a,b),则必能找到正的或负的整数x和y使d = ax + by   
  2. 定理二:若gcd(a,b) = 1,则方程ax≡c(mod b)在[0,b-1]上有唯一解  
  3. 定理三:若gcd(a,b) = d,则方程ax≡c(mod b)在[0,b|d01]上有唯一解  

于是,对上述方程 ax + by = 1, 当gcd(a,b) != 1的时候无解

故方程ax + by = c 有解的条件是 : c%gcd(a,b) = 0;

按乘法逆元讲,一般,我们能找到无数组解满足条件,但一般题目是求解最小的那组解

假设我们求出了特解x0,那么 ,只需要用x0 % b就是最小解了

为什么?(这个我没管,反正知道是这个。。。。后面贴的参考资料链接里面有。。。)

另外,有的时候求得的特解可能是个负数,或者说b是负数,怎么办?

如果b是负数,取b的绝对值

如果x0是负数,让x0对|b|取模后再加上|b|

然后,直接上模板代码:

[cpp]  view plain  copy
  1. int Cal(int a,int b)//求最小的x使ax+ by = 1  
  2. {  
  3.     int x,y;  
  4.     int gcd = e_gcd(a,b,x,y);  
  5.     if (1%gcd) return -1;//无解  
  6.     x*=1/gcd;  
  7.     b = abs(b);  
  8.     int ans = x%b;  
  9.     if (ans <= 0) ans += b;  
  10.     return ans;  
  11. }  


下面给出求最小逆元的代码:

[cpp]  view plain  copy
  1. typedef long long ll;  
  2. ll e_gcd (ll a, ll b, ll& x, ll& y)  
  3. {  
  4.     if (b == 0)  
  5.     {  
  6.         x = 1, y = 0;  
  7.         return a;  
  8.     }  
  9.     ll ans = e_gcd (b, a % b, y, x);  
  10.     y -= a / b * x; //这个和前面用的方法不一样,不过是对的,写起来更快、  
  11.     return ans;  
  12. }  
  13. ll Cal(ll a,ll b,ll c)//求最小的x使ax+ by = c  
  14. {  
  15.     ll x,y;  
  16.     ll gcd = e_gcd(a,b,x,y);  
  17.     if (c%gcd) return -1;//无解  
  18.     x*=c/gcd;  
  19.     b /= gcd;  
  20.     if (b < 0) b = -b;  
  21.     ll ans = x%b;  
  22.     if (ans <= 0) ans += b;  
  23.     return ans;  
  24. }  

来一道入门级的裸的扩展欧几里德的题感受下模板怎么用:

PS:方程还是要自己列,然后用扩展欧几里德求解

青蛙的约会

AC代码供参考:

[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cmath>  
  5. #define mem(a,x) memset(a,x,sizeof(a))  
  6. using namespace std;  
  7. typedef long long ll;  
  8. ll e_gcd(ll a,ll b,ll &x,ll &y)  
  9. {  
  10.     if (b == 0)  
  11.     {  
  12.         x = 1,y = 0;  
  13.         return a;  
  14.     }  
  15.     ll ans = e_gcd(b,a%b,x,y);  
  16.     ll tmp = x;  
  17.     x = y;  
  18.     y = tmp - a/b*y;  
  19.     return ans;  
  20. }  
  21. ll cal(ll a,ll b,ll c)//求最小的x使ax+by=c  
  22. {  
  23.     ll x,y;  
  24.     ll gcd = e_gcd(a,b,x,y);  
  25.     if (c%gcd != 0) return -1;  
  26.     x *= c/gcd;  
  27.     b/=gcd;  
  28.     if (b < 0) b = -b;  
  29.     ll ans = x%b;  
  30.     if (ans <= 0) ans += b;  
  31.     return ans;  
  32. }  
  33. int main()  
  34. {  
  35.     ll xa,xb,va,vb,L;  
  36.     while (~scanf("%lld %lld %lld %lld %lld",&xa,&xb,&va,&vb,&L))  
  37.     {  
  38.         ll ans = cal(vb-va,L,xa-xb);  
  39.         if (ans == -1) puts("Impossible");  
  40.         else printf("%lld\n",ans);  
  41.     }  
  42.     return 0;  
  43. }  

三、中国剩余定理

基本看到一个算法都是用外国人的名字命名,而中国剩余定理又称孙子定理,没错,就是中国的孙子!(这话怎么怪怪的(⊙o⊙)......)
《孙子算经》里面好像有个求同余方程组的问题,不过古文就不看了。
用现代数学的语言来说,中国剩余定理给出了以下一元线性同余方程组:

[cpp]  view plain  copy
  1.     x ≡ a1 (mod m1)    x%m1 = a1       
  2.     x ≡ a2 (mod m2)    x%m2 = a2  
  3. (S):x ≡ a3 (mod m3) -> x%m3 = a3  
  4.     ...                ...  
  5.     x ≡ an (mod mn)    x%mn = an  

有解的判定条件,并用 构造法给出了在有解的情况下解的具体形式
中国剩余定理说明:假设 整数 m 1 , m 2 , ... , m n 两两互质 ,则对任意的整数: a 1 , a 2 , ... , a n ,方程组(S) 有解,并且通解可以用如下方式构造得到:
设 
 M = m1*m2*m3......*mn = ∏mi(i从1-n)
 是整数 m 1, m 2, ... , m n的乘积,并设 
 Mi = M/mi(i从1到n) 
 是除了 m i以外的 n- 1个整数的乘积。
设 
 ti = Mi^-1
 为 
 Mi
 模 
 mi
 的数论倒数 
 ti*Mi ≡1(mod mi)(i从1到n) 
 :
方程组 
 (S)
 的通解形式为 
 x = a1t1M1 + a2t2M2 + ... + antnMn + kM = kM + ∑aitiMi,(i = 1,2,3,...,n,k ∈ Z) 
 :在模 
 M 
的意义下,方程组 
 (S) 
 只有一个解: 
 x = ∑aitiMi,(i = 1,2,3,...,n)
人生苦短,暂不证明(其实鶸不会证明。。。)
直接上模板代码:
[cpp]  view plain  copy
  1. typedef long long ll;  
  2. ll e_gcd (ll a, ll b, ll& x, ll& y)  
  3. {  
  4.     if (b == 0)  
  5.     {  
  6.         x = 1, y = 0;  
  7.         return a;  
  8.     }  
  9.     ll ans = e_gcd (b, a % b, y, x);  
  10.     y -= a / b * x; //这个和前面用的方法不一样,不过是对的,写起来更快、  
  11.     return ans;  
  12. }  
  13. ll CR(int a[],int m[],int n)  
  14. {  
  15.     ll M = 1;  
  16.     for (int i = 1;i <= n;++i) M*=m[i];  
  17.     ll ans = 0;  
  18.     for (int i = 1;i <= n;++i)  
  19.     {  
  20.         ll Mi = M/m[i];ll x,y;  
  21.         ll t = e_gcd(m[i],Mi,x,y);  
  22.         ans = (ans + y*Mi*a[i])%M;  
  23.     }  
  24.     return (M+ans%M)%M;  
  25. }  

=-=这个是我最初的模板,当然他非常渣,在经历了WA无数之后,模板得到了很好的改进,
就不直接贴了,直接上入门题目和AC代码(AC代码用的改进版模板=-=)
题目: Hello Kiki 
代码:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<algorithm>  
  3. #include<cstdio>  
  4. #include<cstring>  
  5. #include<cmath>  
  6. #define mem(a,x) memset(a,x,sizeof(a))  
  7. using namespace std;  
  8. typedef long long ll;  
  9. ll e_gcd (ll a, ll b, ll& x, ll& y)  
  10. {  
  11.     if (b == 0)  
  12.     {  
  13.         x = 1, y = 0;  
  14.         return a;  
  15.     }  
  16.     ll ans = e_gcd (b, a % b, y, x);  
  17.     y -= a / b * x;  
  18.     return ans;  
  19. }  
  20. ll CR (int a[], int m[], int n)  
  21. {  
  22.     ll Mi = m[1], ans = a[1];  
  23.     for (int i = 2; i <= n; ++i)  
  24.     {  
  25.         ll mi = m[i], ai = a[i];  
  26.         ll x, y;  
  27.         ll gcd = e_gcd (Mi, mi, x, y);  
  28.         ll c = ai - ans;  
  29.         if (c % gcd != 0) return -1;  
  30.         ll M = mi / gcd;  
  31.         ans += Mi * ( ( (c /gcd*x) % M + M) % M);  
  32.         Mi *= M;  
  33.     }  
  34.     if (ans == 0) //当余数都为0  
  35.     {  
  36.         ans = 1;  
  37.         for (int i = 1; i <= n; ++i)  
  38.         {  
  39.             ans = ans*m[i]/__gcd(ans,(ll)m[i]);  
  40.         }  
  41.     }  
  42.     return ans;  
  43. }  
  44. int main()  
  45. {  
  46.     int T;cin>>T;int kas = 0;  
  47.     while (T--)  
  48.     {  
  49.         int n,a[100],m[100];  
  50.         scanf("%d",&n);  
  51.         for (int i = 1;i <= n;++i) scanf("%d",m+i);  
  52.         for (int i = 1;i <= n;++i) scanf("%d",a+i);  
  53.         printf("Case %d: %lld\n",++kas,CR(a,m,n));  
  54.     }  
  55.     return 0;  
  56. }  

不爽再来一题? Biorhythms
这题要想清楚同余方程组怎么用的,本鶸开始没想清楚直接套模板使劲WA。。。
AC代码:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<stdlib.h>  
  5. #include<cmath>  
  6. #include<algorithm>  
  7. #define mem(a,x) memset(a,x,sizeof(a))  
  8. using namespace std;  
  9. typedef long long ll;  
  10. ll e_gcd(ll a,ll b,ll &x,ll &y)  
  11. {  
  12.     if (b == 0)  
  13.     {  
  14.         x = 1,y = 0;  
  15.         return a;  
  16.     }  
  17.     ll ans = e_gcd(b,a%b,y,x);  
  18.     y -= a/b*x;  
  19.     return ans;  
  20. }  
  21. ll CR(int a[],int m[],int n)  
  22. {  
  23.     ll M = 1;  
  24.     for (int i = 1;i <= n;++i) M*=m[i];  
  25.     ll ans = 0;  
  26.     for (int i = 1;i <= n;++i)  
  27.     {  
  28.         ll Mi = M/m[i]; ll x,y;  
  29.         ll t = e_gcd(m[i],Mi,x,y);  
  30.         ans = (ans+y*Mi*a[i])%M;  
  31.     }  
  32.     ans = (M+ans%M)%M;  
  33.     if (ans == 0) //当余数都为0  
  34.     {  
  35.         ans = 1;  
  36.         for (int i = 1; i <= n; ++i)  
  37.         {  
  38.             ans = ans*m[i]/__gcd(ans,(ll)m[i]);  
  39.         }  
  40.     }  
  41.     return ans;  
  42. }  
  43. int main()  
  44. {  
  45.     int a[4];  
  46.     int m[4] = {0,23,28,33};int d;int kas = 0;  
  47.     while (~scanf("%d %d %d %d",a+1,a+2,a+3,&d))  
  48.     {  
  49.         if (a[1]==-1&&a[2]==-1&&a[3]==-1&&d==-1) break;  
  50.         for (int i = 1;i <= 3;++i)  
  51.         {  
  52.             a[i] = a[i] - d;  
  53.             while (a[i]<0) a[i]+=m[i];  
  54.         }  
  55.         ll ans = CR(a,m,3);  
  56.         printf("Case %d: the next triple peak occurs in %lld days.\n",++kas,ans);  
  57.     }  
  58.     return 0;  
  59. }  

四、素数筛法
很多数学题里面都有用到,主要就是卡超时的问题。
不多说,直接上三种模板:
素数筛法一:(最简单的)
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. #define mem(a,x) memset(a,x,sizeof(a))  
  5. #define inf (1<<29)  
  6. using namespace std;  
  7. typedef long long ll;  
  8. const int N = 100;  
  9. bool p[N+5];  
  10. void init()  
  11. {  
  12.     mem(p,1);  
  13.     for (int i = 2;i*i <= N;++i)  
  14.     {  
  15.         if (p[i])  
  16.         {  
  17.             for (int j = i*i;j <= N;j+=i)  
  18.             {  
  19.                 p[j] = 0;  
  20.             }  
  21.         }  
  22.     }  
  23. }  
  24. int main()  
  25. {  
  26.     init();  
  27.     for (int i = 2;i <= N;++i)  
  28.     {  
  29.         if (p[i]) cout<<i<<endl;  
  30.     }  
  31.     return 0;  
  32. }  
素数筛法二:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cmath>  
  3. #include<cstdio>  
  4. #define mem(a,x) memset(a,x,sizeof(a))  
  5. #define inf (1<<29)  
  6. using namespace std;  
  7. typedef long long ll;  
  8. const int N = 100;  
  9. int p[N+5];  
  10. void init()  
  11. {  
  12.     int sq = (int)sqrt(N*2+1);  
  13.     for (int i = 3;i <= sq;i+=2)  
  14.     {  
  15.         if (p[i>>1]) continue;  
  16.         for (int j = i*i;j <= N<<1;j+=i<<1)  
  17.         {  
  18.             p[j>>1] = 1;  
  19.         }  
  20.     }  
  21. }  
  22. int main()  
  23. {  
  24.     init();  
  25.     puts("2");  
  26.     for (int i = 1;i < N;++i)  
  27.     {  
  28.         if (p[i]) continue;  
  29.         printf("%d\n",(i<<1)+1);  
  30.     }  
  31.     return 0;  
  32. }  

素数筛法三:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstring>  
  3. #include<cstdio>  
  4. #define mem(a,x) memset(a,x,sizeof(a))  
  5. #define inf (1<<29)  
  6. using namespace std;  
  7. typedef long long ll;  
  8. const int N = 100000;  
  9. int a[N+5];  
  10. int p[N+5];  
  11. void init()  
  12. {  
  13.     int t = 0;  
  14.     for (int i = 2;i <= N;++i)  
  15.     {  
  16.         if (a[i] == 0)  
  17.         {  
  18.             p[++t] = i;  
  19.         }  
  20.         for (int j = 1,k;(j<=t)&&(k=i*p[j])<=N;++j)  
  21.         {  
  22.             a[k] = 1;  
  23.             if (i%p[j]==0) break;  
  24.         }  
  25.     }  
  26. }  
  27. int main()  
  28. {  
  29.     init();  
  30.     for (int i = 1;p[i]>1;++i)  
  31.     {  
  32.         printf("%d\n",p[i]);  
  33.     }  
  34.     return 0;  
  35. }  

本来素数筛完应该直接上欧拉函数,不过觉得大素数测定蛮好玩的,于是插一句大素数测定与整数分解
五、大素数测定与整数分解:
上面的素数筛法很常用,但是如果问你一个很大的数n(n < 2^63)是不是素数,怎么办?
于是引入了 Miller Rabin算法
该算法是一种基于概率的素数测试算法,特点是速度快,能判断<2^63的数是不是素数
虽然是基于概率,但是其实该算法还是蛮可靠的,首先是可以通过增加测试次数提高测试的正确率
另外,我自己玩的时候,将测试次数调为1,一直测,没有出现判断错误的情况(可能我人品太好(⊙o⊙)?)
原理大致看了下,表示不是很懂,直接上代码
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<string>  
  5. #include<algorithm>  
  6. #include<queue>  
  7. #include<cmath>  
  8. #include<stdlib.h>  
  9. #include<cctype>  
  10. #include<time.h>  
  11. #define mem(a,x) memset(a,x,sizeof(a))  
  12. using namespace std;  
  13. typedef long long ll;  
  14. const int S = 8;//测试次数  
  15. ll mult_mod (ll a,ll b, ll c)  
  16. {  
  17.     a%=c,b%=c;  
  18.     ll ret = 0;  
  19.     ll tmp = a;  
  20.     while (b)  
  21.     {  
  22.         if (b&1)  
  23.         {  
  24.             ret += tmp;  
  25.             if (ret > c) ret -= c;  
  26.         }  
  27.         tmp<<=1;  
  28.         if (tmp>c) tmp-=c;  
  29.         b>>=1;  
  30.     }  
  31.     return ret;  
  32. }  
  33. ll pow_mod(ll a,ll n,ll mod)  
  34. {  
  35.     ll ret = 1;  
  36.     ll temp = a%mod;  
  37.     while (n)  
  38.     {  
  39.         if (n&1) ret = mult_mod(ret,temp,mod);  
  40.         temp = mult_mod(temp,temp,mod);  
  41.         n>>=1;  
  42.     }  
  43.     return ret;  
  44. }  
  45. bool check(ll a,ll n,ll x,ll t)  
  46. {  
  47.     ll ret = pow_mod(a,x,n);  
  48.     ll last = ret;  
  49.     for (int i = 1;i <= t;++i)  
  50.     {  
  51.         ret = mult_mod(ret,ret,n);  
  52.         if (ret == 1&&last!=1&&last!=n-1) return 1;  
  53.         last = ret;  
  54.     }  
  55.     if (ret != 1) return 1;  
  56.     else return 0;  
  57. }  
  58. bool MR(ll n)  
  59. {  
  60.     if(n < 2) return 0;  
  61.     if (n == 2) return 1;  
  62.     if ((n&1)==0) return 0;  
  63.     ll x = n - 1;  
  64.     ll t = 0;  
  65.     while ((x&1)==0)  
  66.     {  
  67.         x>>=1;++t;  
  68.     }  
  69.     srand(time(NULL));  
  70.     for (int i = 0;i < S;++i)//做S次测试  
  71.     {  
  72.         ll a = rand()%(n-1) + 1;  
  73.         if (check(a,n,x,t)) return 0;//只要其中有一次判定是合数就可以确定一定是合数  
  74.     }  
  75.     return 1;  
  76. }  
  77. int main()  
  78. {  
  79.     ll n;  
  80.     while (cin>>n)  
  81.     {  
  82.         if (MR(n)) puts("YES");  
  83.         else puts("NO");  
  84.     }  
  85.     return 0;  
  86. }  
水题: Prime Test
这个直接裸模板,就不贴AC代码了
题外话:表示上面算法蛮好玩,于是某次比赛用该算法打素数表,然后就。。。。。呵呵。。。。。所以觉得这个算法主要就是针对很大的数的素数判断的,如果没有这个条件,请贪玩的童鞋不要在比赛随意使用。。。。。。。。
然后整数分解用的是Pollard rho算法,具体原理我也没懂,反正就是可以求2^63以内的数分解成素因子
模板是直接copy的kuangbin的模板(蛮长的。。。。)
[cpp]  view plain  copy
  1. #include<stdio.h>  
  2. #include<string.h>  
  3. #include<stdlib.h>  
  4. #include<time.h>  
  5. #include<iostream>  
  6. #include<algorithm>  
  7. using namespace std;  
  8.   
  9.   
  10. //****************************************************************  
  11. // Miller_Rabin 算法进行素数测试  
  12. //速度快,而且可以判断 <2^63的数  
  13. //****************************************************************  
  14. const int S=20;//随机算法判定次数,S越大,判错概率越小  
  15. //计算 (a*b)%c.   a,b都是long long的数,直接相乘可能溢出的  
  16. //  a,b,c <2^63  
  17. long long mult_mod(long long a,long long b,long long c)  
  18. {  
  19.     a%=c;  
  20.     b%=c;  
  21.     long long ret=0;  
  22.     while(b)  
  23.     {  
  24.         if(b&1){ret+=a;ret%=c;}  
  25.         a<<=1;  
  26.         if(a>=c)a%=c;  
  27.         b>>=1;  
  28.     }  
  29.     return ret;  
  30. }  
  31. //计算  x^n %c  
  32. long long pow_mod(long long x,long long n,long long mod)//x^n%c  
  33. {  
  34.     if(n==1)return x%mod;  
  35.     x%=mod;  
  36.     long long tmp=x;  
  37.     long long ret=1;  
  38.     while(n)  
  39.     {  
  40.         if(n&1) ret=mult_mod(ret,tmp,mod);  
  41.         tmp=mult_mod(tmp,tmp,mod);  
  42.         n>>=1;  
  43.     }  
  44.     return ret;  
  45. }  
  46.   
  47. //以a为基,n-1=x*2^t      a^(n-1)=1(mod n)  验证n是不是合数  
  48. //一定是合数返回true,不一定返回false  
  49. bool check(long long a,long long n,long long x,long long t)  
  50. {  
  51.     long long ret=pow_mod(a,x,n);  
  52.     long long last=ret;  
  53.     for(int i=1;i<=t;i++)  
  54.     {  
  55.         ret=mult_mod(ret,ret,n);  
  56.         if(ret==1&&last!=1&&last!=n-1) return true;//合数  
  57.         last=ret;  
  58.     }  
  59.     if(ret!=1) return true;  
  60.     return false;  
  61. }  
  62.   
  63. // Miller_Rabin()算法素数判定  
  64. //是素数返回true.(可能是伪素数,但概率极小)  
  65. //合数返回false;  
  66.   
  67. bool Miller_Rabin(long long n)  
  68. {  
  69.     if(n<2)return false;  
  70.     if(n==2)return true;  
  71.     if((n&1)==0) return false;//偶数  
  72.     long long x=n-1;  
  73.     long long t=0;  
  74.     while((x&1)==0){x>>=1;t++;}  
  75.     for(int i=0;i<S;i++)  
  76.     {  
  77.         long long a=rand()%(n-1)+1;//rand()需要stdlib.h头文件  
  78.         if(check(a,n,x,t))  
  79.             return false;//合数  
  80.     }  
  81.     return true;  
  82. }  
  83.   
  84.   
  85. //************************************************  
  86. //pollard_rho 算法进行质因数分解  
  87. //************************************************  
  88. long long factor[100];//质因数分解结果(刚返回时是无序的)  
  89. int tol;//质因数的个数。数组小标从0开始  
  90.   
  91. long long gcd(long long a,long long b)  
  92. {  
  93.     if(a==0)return 1;//???????  
  94.     if(a<0) return gcd(-a,b);  
  95.     while(b)  
  96.     {  
  97.         long long t=a%b;  
  98.         a=b;  
  99.         b=t;  
  100.     }  
  101.     return a;  
  102. }  
  103.   
  104. long long Pollard_rho(long long x,long long c)  
  105. {  
  106.     long long i=1,k=2;  
  107.     long long x0=rand()%x;  
  108.     long long y=x0;  
  109.     while(1)  
  110.     {  
  111.         i++;  
  112.         x0=(mult_mod(x0,x0,x)+c)%x;  
  113.         long long d=gcd(y-x0,x);  
  114.         if(d!=1&&d!=x) return d;  
  115.         if(y==x0) return x;  
  116.         if(i==k){y=x0;k+=k;}  
  117.     }  
  118. }  
  119. //对n进行素因子分解  
  120. void findfac(long long n)  
  121. {  
  122.     if(Miller_Rabin(n))//素数  
  123.     {  
  124.         factor[tol++]=n;  
  125.         return;  
  126.     }  
  127.     long long p=n;  
  128.     while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);  
  129.     findfac(p);  
  130.     findfac(n/p);  
  131. }  
  132.   
  133. int main()  
  134. {  
  135.     //srand(time(NULL));//需要time.h头文件//POJ上G++不能加这句话  
  136.     long long n;  
  137.     while(scanf("%I64d",&n)!=EOF)  
  138.     {  
  139.         tol=0;  
  140.         findfac(n);  
  141.         for(int i=0;i<tol;i++)printf("%I64d ",factor[i]);  
  142.         printf("\n");  
  143.         if(Miller_Rabin(n))printf("Yes\n");  
  144.         else printf("No\n");  
  145.     }  
  146.     return 0;  
  147. }  

整数分解也有个题: GCD & LCM Inverse(用dfs去找答案)
AC代码:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<string>  
  5. #include<algorithm>  
  6. #include<queue>  
  7. #include<cmath>  
  8. #include<stdlib.h>  
  9. #include<cctype>  
  10. #define mem(a,x) memset(a,x,sizeof(a))  
  11. using namespace std;  
  12. typedef unsigned long long ll;  
  13. const int S = 20;  
  14. ll mult_mod (ll a, ll b, ll c)  
  15. {  
  16.     a %= c, b %= c;  
  17.     ll ret = 0;  
  18.     ll tmp = a;  
  19.     while (b)  
  20.     {  
  21.         if (b & 1)  
  22.         {  
  23.             ret += tmp;  
  24.             if (ret > c) ret -= c;  
  25.         }  
  26.         tmp <<= 1;  
  27.         if (tmp > c) tmp -= c;  
  28.         b >>= 1;  
  29.     }  
  30.     return ret;  
  31. }  
  32. ll pow_mod (ll a, ll n, ll mod)  
  33. {  
  34.     ll ret = 1;  
  35.     ll temp = a % mod;  
  36.     while (n)  
  37.     {  
  38.         if (n & 1) ret = mult_mod (ret, temp, mod);  
  39.         temp = mult_mod (temp, temp, mod);  
  40.         n >>= 1;  
  41.     }  
  42.     return ret;  
  43. }  
  44. bool check (ll a, ll n, ll x, ll t)  
  45. {  
  46.     ll ret = pow_mod (a, x, n);  
  47.     ll last = ret;  
  48.     for (int i = 1; i <= t; ++i)  
  49.     {  
  50.         ret = mult_mod (ret, ret, n);  
  51.         if (ret == 1 && last != 1 && last != n - 1) return 1;  
  52.         last = ret;  
  53.     }  
  54.     if (ret != 1) return 1;  
  55.     else return 0;  
  56. }  
  57. bool MR (ll n)  
  58. {  
  59.     if (n < 2) return 0;  
  60.     if (n == 2) return 1;  
  61.     if ( (n & 1) == 0) return 0;  
  62.     ll x = n - 1;  
  63.     ll t = 0;  
  64.     while ( (x & 1) == 0)  
  65.     {  
  66.         x >>= 1;  
  67.         ++t;  
  68.     }  
  69. //    srand(time(NULL));  
  70.     for (int i = 0; i < S; ++i) //做S次测试  
  71.     {  
  72.         ll a = rand() % (n - 1) + 1;  
  73.         if (check (a, n, x, t) ) return 0; //只要其中有一次判定是合数就可以确定一定是合数  
  74.     }  
  75.     return 1;  
  76. }  
  77. ll fac[11111];//质因数分解结果(刚返回时时无序的)  
  78. int tot;//质因数的个数,数组下标从0开始  
  79. ll gcd (ll a, ll b)  
  80. {  
  81.     if (a == 0) return 1;  
  82.     if (a < 0) return gcd (-a, b);  
  83.     while (b)  
  84.     {  
  85.         ll t = a % b;  
  86.         a = b, b = t;  
  87.     }  
  88.     return a;  
  89. }  
  90. ll PR (ll x, ll c)  
  91. {  
  92.     ll i = 1, k = 2;  
  93.     ll x0 = rand() % x;  
  94.     ll y = x0;  
  95.     while (1) //美丽的循环,不会死  
  96.     {  
  97.         ++i;  
  98.         x0 = (mult_mod (x0, x0, x) + c) % x;  
  99.         ll d = gcd (y - x0, x);  
  100.         if (d != 1 && d != x) return d;  
  101.         if (y == x0) return x;  
  102.         if (i == k)  
  103.         {  
  104.             y = x0;  
  105.             k += k;  
  106.         }  
  107.     }  
  108. }  
  109. void findfac (ll n) //对n进行素因子分解  
  110. {  
  111.     if (MR (n) ) //如果n是素数  
  112.     {  
  113.         fac[tot++] = n;  
  114.         return;  
  115.     }  
  116.     ll p = n;  
  117.     while (p >= n) p = PR (p, rand() % (n - 1) + 1);  
  118.     findfac (p);  
  119.     findfac (n / p);  
  120. }  
  121. ll x[111];  
  122. ll k;  
  123. ll a, b, mn;  
  124. ll ans;  
  125. ll g, lcm;  
  126. const ll inf = 1LL << 62LL;  
  127. bool fd;  
  128. void dfs (ll cur, ll p)  
  129. {  
  130.     ll q = ans / p;  
  131.     if (gcd (p, q) == 1)  
  132.     {  
  133.         fd = 1;  
  134.         ll tmp = p * g + q * g;  
  135.         if (mn > tmp)  
  136.         {  
  137.             mn = tmp;  
  138.             a = p*g;  
  139.             b = q*g;  
  140.         }  
  141.     }  
  142.     if (cur > k) return;  
  143.     dfs(cur+1,p);p*=x[cur];  
  144.     if (p > mn ) return ;  
  145.     dfs(cur+1,p);  
  146. }  
  147. int main()  
  148. {  
  149.     while (~scanf ("%llu %llu", &g, &lcm) )  
  150.     {  
  151.         if (g == lcm)  
  152.         {  
  153.             printf("%llu %llu\n",g,lcm);  
  154.             continue;  
  155.         }  
  156.         ans = lcm / g;  
  157.         tot = 0;  
  158. //        cout << ans << endl;  
  159.         findfac (ans);  
  160.         sort (fac, fac + tot); //fac保存的是ans的素因子,比如3 60对应的fac数组是2 2 5  
  161. //        for (int i = 0; i < tot; ++i) cout << fac[i] << " ";  
  162. //        cout << "---------------------------------------------------" << endl;  
  163.         k = 0;  
  164.         x[0] = fac[0];  
  165.         for (int i = 1; i < tot; ++i)  
  166.         {  
  167.             if (fac[i] == fac[i - 1]) x[k] *= fac[i];  
  168.             else x[++k] = fac[i];  
  169.         }  
  170.         sort (x, x + k + 1); //x保存的是所有不相同的素因子,比如3 60 对应的x数组是4 5  
  171. //        for (int i = 0; i <= k; ++i) cout << x[i] << " ";  
  172. //        cout << endl;  
  173.         //用dfs将数组x分成2部分p*q,满足p,q互质,找到所有p,q中使a+b最小的情况,其中a = p*lcm,b = q*lcm  
  174.         //比如3 60 ans = 20 4 5 ,a = 4*3 b = 5*3  
  175.         mn = inf;  
  176.         fd = 0;  
  177.         dfs (0, 1);  
  178.         if (a > b) swap (a, b);  
  179. //        if (!fd) puts ("???");  
  180.         printf ("%llu %llu\n", a, b);  
  181.     }  
  182.     return 0;  
  183. }  
  184. /* 
  185. 7 635040 
  186.  
  187. */  


六、 欧拉函数
定义:
在数论,对正整数n,欧拉函数是小于等于n的数中与n互质的数的数目。
此函数以其首名研究者欧拉命名(Euler'so totient function),它又称为Euler's totient function、φ函数、欧拉商数等。
 例如φ(8)=4,因为1,3,5,7均和8互质。 
通式: 
 φ(x) = x(1 - 1/p1)(1 - 1/p2)(1 - 1/p3) ...(1 - 1/pn)
 ,其中p1, p2……pn为x的所有质因数,x是不为0的整数。φ(1)=1(唯一和1 互质的数(小于等于1)就是1本身)。 (注意:每种质因数只一个。比如12=2*2*3那么φ(12)=12*(1-1/2)*(1-1/3)=4
若n是质数p的k次幂, 
 φ(n) = p^k - p^(k-1) = (p-1)p^(k-1)
 ,因为除了p的倍数外,其他数都跟n互质。
设n为正整数,以 φ(n)表示不超过n且与n互
素的正整数的个数,称为n的欧拉函数值,这里函数
φ:N→N,n→φ(n)称为欧拉函数。
欧拉函数是 积性函数 ——若m,n互质, 
 φ(mn) = φ(m)φ(n);
特殊性质:当n为奇数时, 
 φ(2n) = φ(n); 
 , 证明与上述类似。
若n为质数则 
 φ(n) = n - 1;
数学渣渣,再次直接上模板:
[cpp]  view plain  copy
  1. #include<iostream>  
  2. #include<cmath>  
  3. #include<algorithm>  
  4. using namespace std;  
  5.   
  6. const int MAXN = 1e5;  
  7.   
  8. int isprime[MAXN];  
  9. int prime[MAXN];  
  10. int cnt;  
  11. void getP()  
  12. {  
  13.     cnt = 0;  
  14.     for(int i = 1; i < MAXN; i++)  
  15.         isprime[i] = 1;  
  16.     for(int i = 2; i < MAXN; i++)  
  17.     {  
  18.         if(!isprime[i])continue;  
  19.         prime[cnt++] = i;  
  20.         for(int j = 2 * i; j < MAXN; j += i)  
  21.         {  
  22.             isprime[j] = 0;  
  23.         }  
  24.     }  
  25. }  
  26.   
  27. int euler( int n )  //求小于n且与n互质的数的个数  
  28. {  
  29.     int ans = n;  
  30.   
  31.     for(int i = 0; prime[i] * prime[i] <= n; i++)  
  32.     {  
  33.         if(n%prime[i] == 0)  
  34.         {  
  35.             ans = ans - ans / prime[i]; //ans=ans*(1-1/pi)  
  36.             while(n%prime[i] == 0)  
  37.             {  
  38.                 n /= prime[i];  
  39.             }  
  40.         }  
  41.     }  
  42.     if(n > 1)  ans = ans - ans / n;  
  43.     return ans;  
  44. }  
  45. int main()  
  46. {  
  47.     getP();  
  48.     int n;  
  49.     while(cin >> n&&n)  
  50.     {  
  51.         cout << euler( n ) << endl;  
  52.     }  
  53.     return 0;  
  54. }  

猜你喜欢

转载自blog.csdn.net/FZdeAmbition/article/details/80086426