1. 程式人生 > 實用技巧 >數論知識彙總

數論知識彙總

一 初級預備知識

  1.唯一分解定理:把正整數n寫成質數的乘積(即n=p1p2p3...pk,其中pi為質數且單調不減),這樣的表示是唯一的。

    •例題:[CF 776B]Sherlock and his girlfriend

    •n個點,標號2..n+1,     •給這些點染色,要求若a是b的質因子,則a和b的顏色不同。     •求一種顏色數最少的方案     解:我們只需要2種顏色即可,質數一種顏色,合數一種顏色就可以了。

  2.整除

     設a,b是兩個正整數,且b!=0,則存在唯一的整數q和r,使 a=qb+r,這個式子叫做帶餘除法

     性質:

        •1整除任何數,任何數都整除0         •若a|b,a|c,則a|b+c, a|b-c         •若a|b,則對任意整數c,a|bc         •傳遞性:若a|b,b|c,則a|c

  3.約數

      對於一個大於1正整數n可以分解質因數:n=p1^a1*p2^a2*p3^a3*…*pk^ak,

      則由約數個數定理可知n的正約數有(a₁+1)(a₂+1)(a₃+1)…(ak+1)個,       那麼n的(a₁+1)(a₂+1)(a₃+1)…(ak+1)個正約數的和為       f(n)=(p1^0+p1^1+p1^2+…p1^a1)(p2^0+p2^1+p2^2+…p2^a2)…(pk^0+pk^1+pk^2+…pk^ak)  看計算約數模板之前還有學會素數篩,篩法有很多,二重暴力O(n^2),埃氏篩法,O(nloglogn),尤拉篩O(n).顯然尤拉篩是時間複雜度最低的。 尤拉篩模板:
1
for (int i=2;i<=n;i++) 2 { 3 if (!pri[i])ans[++tot]=i; 4 for (int j=1;(j<=tot)&&(i*ans[j]<=n);j++) 5 { 6 pri[i*ans[j]]=1; 7 if (i%ans[j]==0)break; 8 } 9 }
View Code

我覺得這個部落格講的比較好:https://blog.csdn.net/qq_39763472/article/details/82428602

很難懂的一個點:

    if(i%ans[j]==0)break; 其實打個表出來看看會發現其實意思就是一個非素數只能用它的最小質因數篩掉,避免重複篩,以達到O(n)的複雜度。
 1 #include<bits/stdc++.h>
 2 #define inf 1000000000
 3 #define ll long long
 4 #define N 10000010
 5 using namespace std;
 6 inline int read()
 7 {
 8     int x=0;char ch=getchar();
 9     while (ch<'0'||ch>'9') ch=getchar();
10     while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
11     return x;
12 }
13 int pd[N+10],pri[N];
14 ll e[N+10],t[N+10],sum[N+10];
15 void oula()
16 {
17     for (int i=2;i<=N;i++)
18     {
19         if (!pd[i]) {t[i]=i+1;e[i]=1;pri[++pri[0]]=i;}
20         for (int j=1;j<=pri[0]&&pri[j]*i<=N;j++)
21         {
22             pd[pri[j]*i]=1;
23             if (i%pri[j]==0)
24             {
25                 t[i*pri[j]]=t[i]*pri[j]+e[i];
26                 e[i*pri[j]]=e[i];
27                 break;
28             }
29             t[i*pri[j]]=t[i]*(pri[j]+1),e[i*pri[j]]=t[i];
30         }
31     }
32 }
33 int main()
34 {
35     oula();t[1]=e[1]=1;
36     for (int i=1;i<=N;i++) sum[i]=sum[i-1]+t[i];
37     int T=read();
38     while (T--)
39     {
40         ll n=read();
41         printf("%lld\n",n*n-sum[n]);
42     }
43     return 0;
44 }
計算約數和(線性篩)
 1 #include<cstdio>
 2 
 3 using namespace std;
 4 
 5 #define N 1000001
 6 
 7 bool vis[N];
 8 int prime[N];
 9 
10 int t[N],e[N];
11 
12 int main()
13 {
14     int n;
15     scanf("%d",&n);
16     int cnt=0;
17     t[1]=1;
18     for(int i=2;i<=n;++i)
19     {
20         if(!vis[i])
21         {
22             prime[++cnt]=i;
23             t[i]=2;
24             e[i]=1;
25         }
26         for(int j=1;j<=cnt;++j)
27         {
28             if(i*prime[j]>n) break;
29             vis[i*prime[j]]=true;
30             if(i%prime[j]==0)
31             {
32                 t[i*prime[j]]=t[i]/(e[i]+1)*(e[i]+2);
33                 e[i*prime[j]]=e[i]+1;
34                 break;
35             }
36             else 
37             {
38                 t[i*prime[j]]=t[i]*2;
39                 e[i*prime[j]]=1;
40             }
41         }
42     }
43     long long ans=0;
44     for(int i=1;i<=n;++i) ans+=t[i];
45     printf("%lld",ans);
46 }
計算約數個數(線性篩)

二.正題

1.歐幾里得:

1 int gcd(int a,int b)
2 {
3     return  b==0?a:gcd(b,a%b);
4 }
計算gcd

2.擴充套件歐幾里得:

1 void exgcd(ll a,ll b,ll &x,ll &y)
2 {
3     if(b==0){x=1;y=0;return;}
4     exgcd(b,a%b,x,y);
5     ll t=x;x=y;y=t-a/b*y;
6 }
View Code

3.線性同餘方程組:

  1>中國剩餘定理:只用來解模數互質情況,參見http://blog.miskcoo.com/2014/09/chinese-remainder-theorem

 1 //chu是除數,yu是餘數
 2 //注意只適用於除數兩兩互質
 3 #include<iostream>  
 4 #include<queue>  
 5 using namespace std;  
 6 typedef long long ll;
 7 ll extended_euclid(ll a, ll b, ll &x, ll &y) {
 8     ll d;
 9     if(b == 0) {x = 1; y = 0; return a;}
10     d = extended_euclid(b, a % b, y, x);
11     y -= a / b * x;
12     return d;
13 }
14 ll chinese_remainder(ll b[], ll w[], ll len) { 
15     ll i, d, x, y, m, n, ret;
16     ret = 0; n = 1; 
17     for(i=0; i < len ;i++) n *= w[i];
18     for(i=0; i < len ;i++) { 
19         m = n / w[i];
20         d = extended_euclid(w[i], m, x, y);
21         ret = (ret + y*m*b[i]) % n;
22     }
23     return (n + ret%n) % n;
24 }
25 ll yu[100],chu[100];
26 int main()
27 {
28     ll n;
29     while(cin>>n)
30     {
31         for(ll i=0;i<n;i++)
32         {
33             cin>>chu[i]>>yu[i];
34         }
35         ll ans=chinese_remainder(yu,chu,n);
36         cout<<ans<<endl;
37     }
38     return 0;
39 }
程式碼實現

  2>擴充套件剩餘定理:解模數可以不互質情況,證明參見:http://www.cnblogs.com/zwfymqz/p/8425731.html

 1 #include<cstdio>
 2 #include<iostream>
 3 #define ll long long 
 4 using namespace std;
 5 inline ll read()
 6 {
 7     ll x=0,f=1;char ch=getchar();
 8     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 9     while(ch>='0'&&ch<='9'){x*=10;x+=ch-'0';ch=getchar();}
10     return x*f;
11 }
12 int k;
13 ll a1,b1,a2,b2;
14 inline ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
15 void exgcd(ll a,ll b,ll &x,ll &y)
16 {
17     if(b==0){x=1;y=0;return;}
18     exgcd(b,a%b,x,y);
19     ll t=x;x=y;y=t-a/b*y;
20 }
21 int main()
22 {
23     k=read();
24     a1=read();b1=read();
25     ll a,b,c,x,y;
26     for(int i=1;i<k;i++)
27     {
28         a2=read();b2=read();
29         a=a1;b=a2;c=b2-b1;
30          ll t=gcd(a,b);
31         if(c%t){printf("-1\n");return 0;}
32         else 
33         {
34             a/=t;b/=t;c/=t;
35             exgcd(a,b,x,y);
36             x=((c*x)%b+b)%b;
37             b1=b1+a1*x;
38             a1=a1*b;
39            }
40     }
41     printf("%lld\n",b1);
42     return 0;
43 }
View Code

待續......