NOIP複習之1 數學數論

IBOOM726發表於2018-09-17

noip一輪複習真的要開始啦!!!

大概順序是這樣的

1.數學

2.搜尋貪心

3.資料結構

4.圖論

5.dp

6.其他

                數學

1.數論

數論被稱為數學皇冠上的明珠,他的重要性主要在於它是其他學習的祖師,基本上什麼代數問題都可以通過數論推導,其實有的圖論也是(數學上)。

我們資訊中的數論主要是說對整除同餘的研究~~~~~~~

①:唯一分解定理與素數

這個之前我們先要講素數(定義全部掠過)

素數篩法:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int prime[1000100],phi[1000100],n,m,k,l,a,b;
 6 bool isprime[1000100];
 7 void shai()
 8 {
 9     int sum=0;
10     memset(isprime,0,sizeof(isprime));
11     memset(prime,0,sizeof(prime));
12     for(int i=2;i<=n;i++)
13     {
14         if(!isprime[i])
15         {
16             prime[sum++]=i;
17             phi[i]=i-1;
18         }
19         for(int j=0;j<sum&&prime[j]*i<=n;j++)
20         {
21             isprime[prime[j]*i]=1;
22             if(i%prime[j]==0)
23             {
24                 phi[i*prime[j]]=phi[i]*prime[j];
25                 break;
26             }
27             else
28             {
29                 phi[i*prime[j]]=phi[i]*(prime[j]-1);
30             }
31         }
32     }
33 } 
34 int main()
35 {
36     phi[1]=1;
37     cin>>n;
38     shai();
39     for(int i=0;i<=n;i++)
40     cout<<phi[i]<<" ";
41     return 0; 
42 }

這裡面也有一個叫phi的東西。。。我們一會再說。

篩好以後我們就可以根絕唯一分解定理分解素數

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int prime[10000100],phi[10000100],cut[10000100][3],num=0,n,m,k,l,a,b;
 6 bool isprime[10000100];
 7 void shai()
 8 {
 9     int sum=0;
10     memset(isprime,0,sizeof(isprime));
11     memset(prime,0,sizeof(prime));
12     for(int i=2;i<=10000000;i++)
13     {
14         if(!isprime[i])
15         {
16             prime[sum++]=i;
17             phi[i]=i-1;
18         }
19         for(int j=0;j<sum&&prime[j]*i<=n;j++)
20         {
21             isprime[prime[j]*i]=1;
22             if(i%prime[j]==0)
23             {
24                 phi[i*prime[j]]=phi[i]*prime[j];
25                 break;
26             }
27             else
28             {
29                 phi[i*prime[j]]=phi[i]*(prime[j]-1);
30             }
31         }
32     }
33 } 
34 int main()
35 {
36     phi[1]=1;
37     shai();
38     cin>>m;
39     for(int i=0;i<=10000000;i++){
40         if(m==1)break;
41         while(m%prime[i]==0)m/=prime[i],cut[i][1]=prime[i],cut[i][2]++;
42     }
43     for(int i=0;i<=1000;i++){
44         if(cut[i][1]!=0)
45         cout<<cut[i][1]<<" "<<cut[i][2]<<endl;
46     }
47     return 0; 
48 }

省選之後我們發現o跟下n求一個數的唯一分解是不夠的

於是就有了miller rabin。

這個東西是根據那個費馬小定理(尤拉定理)的p為素數的條件進行的素數測試。

這個東西有個兄弟叫rho

這個東西是用來解決找合數因子的

把主程式裡的不斷除m改為知道miler檢測到為素數為止。

好了那麼我們就可以對10^18次方之內的進行分解了(noip只會考on)

那麼有什麼用呢?

我們把分解後的表示為π(ai^pi)

1.因子數:π(pi+1);

2.因子和:這個很有意思:

ans=(1+p1^1+p1^2+……+p1^a1)(1+p2^1+p2^2+……+p2^a2)(1+p3^1+p3^2+……+p3^a3)……(1+pn^1+pn^2+……+pn^an)

=π(1-pi^ai)/(1-pi);

這個東西逆元就行。

②快速冪(模數的最廣泛應用(二進位制))

 1 //ksm
 2  #include<iostream>
 3  #include<cstdio>
 4  #include<algorithm>
 5  #include<queue>
 6  #include<stack>
 7  #include<fstream>
 8  #include<cstring>
 9  using namespace std;
10  #define ll long long
11  ll qpow(ll a,ll x)
12  {
13      ll base=a,ans=1;
14      for(;x>0;x>>=1)//簡單辦法 
15      {if(x&1)
16          ans*=base;//當為奇數疊加求解 
17          base*=base;
18      }
19      return ans;
20  } 
21   int main()
22  {
23      //freopen(".in","r",stdin);
24      //freopen(".in","r",stdin);
25      ll a,b;
26      cin>>a>>b;
27      cout<<qpow(a,b);
28      return 0;
29  }

這個東西根據費馬小定理可以求逆元

方法是a^(mod-2)=a^(-1)mod mod;

快速乘其實是龜速乘

利用二進位制實現調整中途溢位long long的膜意義下乘法

也是利用二進位制表示

 這樣就可解決10^18次方的兩個數在%意義下的乘法取模

(其實它是log length的複雜度,並不快,只是比n個相加取模要快(傳統防止溢位)所以不是像noi2018龍和勇士那種問題不要亂用)

 1 #include<iostream>
 2 #define mod 1000000007
 3 #define ll long long
 4 using namespace std;
 5 ll ksc(ll a,ll b){
 6     ll ans=0,base=a;
 7     for(;b;b>>=1){
 8         if(b&1)ans=(ans+base)%mod;
 9         base=base*2%mod;
10     }
11     return ans;
12 }
13 ll a,b; 
14 int main(){
15     cin>>a>>b;
16     cout<<ksc(a,b);
17     return 0;
18 } 

 

③exgcd gcd

gcd太熟悉了現打一個

1 long long gcd(long long a,long long b){
2     return !b?a:gcd(b,a%b);
3 }

exgcd也差不多 exgcd也可以求逆元的,這個定義式推。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int gcd(int a,int b)
 6 {
 7     return b==0?a:gcd(b,a%b);
 8 }
 9 void exgcd(int a,int b,int &d,int &x,int &y)
10 {
11     if(!b){d=a;x=1;y=0;return ;}
12     exgcd(b,a%b,d,y,x);
13     y-=(a/b)*x;
14 }
15 int main ()
16 {
17     int m,n,k;
18     cin>>m>>n>>k;
19     int gc=gcd(n,m);
20     m/=gc;n/=gc;
21     k/=gc;
22     int x,y,d;
23     cout<<m<<"x+"<<n<<"y="<<gc<<endl;
24     exgcd(m,n,d,x,y);
25     cout<<x<<" "<<y<<endl<<d<<endl<<endl;
26     for(int i=1;i<=10;i++)
27     {
28         x+=(n)*i;
29         y-=(m)*i;
30         cout<<x<<" "<<y<<endl;
31     }
32     return 0;
33 }

④excrt。crt

excrt 在noip是在不大可能考,要考也是t3的最後一部分,畢竟noi t1難度不是蓋的(當時本蒟蒻只拿了三十分那個題。。。。)

 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<ctime>
 4 #include<cstdio>
 5 #include<cstring>
 6 using namespace std;
 7 long long m[100000],ma[100000],a[100000],c,mall=1,ansx;
 8 long long qpowmod(long long a,long long x,long long p)
 9 {
10     long long base=a,ans=1;
11     for(;x>0;x>>=1){
12         if(x&1)
13         ans*=base,ans%=p;
14         base*=base,base%=p;
15     }
16     return ans%p;
17 }
18 int main()
19 {
20     
21     cin>>c;
22     for(int i=1;i<=c;i++){
23         cin>>a[i]>>m[i];
24         mall*=m[i];
25     }
26     for(int i=1;i<=c;i++){
27         ma[i]=mall/m[i];
28     }
29     for(int i=1;i<=c;i++){
30         ansx+=a[i]*ma[i]*qpowmod(ma[i],m[i]-2,mall);
31         ansx%=mall;
32     }
33      
34     cout<<mall<<endl<<ansx<<endl;
35 } 

excrt的話就是加一個exgcd就行。

 

另外 我們需要利用bezout解決問題比如小凱的疑惑

幾個簡單而熟知的結論

(a,b)=(a+b,b)=(a-b,b)

ax+by=(a,b) x,y∈z

 

 

2.計算幾何

計算幾何noip要是考也是向量題,最多帶個凸包。

①向量點積a2b1-a1b2

②凸包:這個東西在斜率優化那裡有(那裡是個上凸殼),大概是完全一樣的。棧維護進出

保證點積>0

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cmath>
 5 using namespace std;
 6 struct node{
 7     double x,y;
 8 }p[10005],s[10005];
 9 int n;
10 double ans,mid;
11 double CJ(node a1,node a2,node b1,node b2)
12 {
13     return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);
14 }
15 double dis(node p1,node p2)
16 {
17     return sqrt( (double)(p2.y-p1.y)*(p2.y-p1.y)*1.0+(double)(p2.x-p1.x)*(p2.x-p1.x)*1.0 );
18 }
19 bool cmp(node p1,node p2)
20 {
21     double tmp=CJ(p[1],p1,p[1],p2);
22     if(tmp>0) return 1;
23     if(tmp==0 && dis(p[0],p1)<dis(p[0],p2)) return 1;
24     return 0;
25 }
26 int main()
27 {
28     scanf("%d",&n);
29     for(int i=1;i<=n;++i)
30     {
31         scanf("%lf%lf",&p[i].x,&p[i].y);
32         if(i!=1&&p[i].y<p[1].y)
33         {
34             mid=p[1].y;p[1].y=p[i].y;p[i].y=mid;
35             mid=p[1].x;p[1].x=p[i].x;p[i].x=mid;
36         }
37     }  
38 
39     sort(p+2,p+1+n,cmp);
40     s[1]=p[1];
41     int tot=1;
42     for(int i=2;i<=n;i++)
43     {
44         while(tot>1&&CJ(s[tot-1],s[tot],s[tot],p[i])<=0) tot--;
45         tot++;
46         s[tot]=p[i];
47     }
48     s[tot+1]=p[1];
49     for(int i=1;i<=tot;i++) ans+=dis(s[i],s[i+1]);
50     printf("%.2lf
",ans);
51     return 0;
52 }

 

3.遞推,矩陣初步與數列

①幾個有趣的特殊數列

②ologn求通項的幾(兩)種方法(noipt2t3){

1.特徵根

這個東西沒有任何程式碼。。。就是求數列通項。

通過特徵方程解出跟

然後帶a1,a2,a3……an進去

可以看看這個

https://wenku.baidu.com/view/6e270e87284ac850ac024203.html

2.矩陣快速冪

這個十分好理解(在矩陣乘法理解的基礎上)我們可以認為數列是一個行向量

而一個n階遞推數列對應一個列向量,所以因此我們就需要找到一個由前一個列向量遞推到下一個的矩陣。

這個矩陣可以記一個結論

它是 對於n階矩陣 ai為係數

n*n大小

[a1,a2,a3,a4,……,an]

[10000000000……00]

[010000000……00]

[0010000000……00]

[00010000000……00]

[000010000000……00]

.

.

[0………………1]

然後 矩陣快速冪!!!

就是矩陣乘法代替那個快速冪的乘就行

程式碼!!!

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int n,k;
 6 struct mix{
 7     int a[105][105];
 8 };
 9 mix mx (mix a1,mix a2,int n)
10     {    
11         mix a3;int ans=0;
12         memset(a3.a,0,sizeof(a3.a));
13         for(int i=1;i<=n;i++)
14         for(int j=1;j<=n;j++)
15         {
16             ans=0;
17         for(int k=1;k<=n;k++)
18         {
19             ans+=a1.a[i][k]*a2.a[k][j];
20         }
21         a3.a[i][j]=ans; 
22         } 
23         return a3;
24     }
25 mix ksm(mix a,int x)
26 {
27     mix ans;
28     memset(ans.a,0,sizeof(ans.a));
29     for(int i=1;i<=n;i++)
30     ans.a[i][i]=1;
31     for(;x;x>>=1)
32     {
33         if(x&1)
34         mx(ans,a,n);
35         mx(a,a,n);
36     }
37     return ans;
38 } 
39 int main()
40 {
41     mix m1;
42     cin>>n>>k;
43     cout<<endl<<n<<endl<<k;
44     for(int i=1;i<=n;i++)
45     for(int j=1;j<=n;j++)
46     cin>>m1.a[i][j];
47         
48     mix m=ksm(m1,k);
49     for(int i=1;i<=n;i++)
50     {
51         cout<<endl;
52     for(int j=1;j<=n;j++)
53         {
54             cout<<m.a[i][j]<<" ";
55         }
56     }
57     cout<<endl<<n<<endl<<n;
58     return 0;
59 }
60  

 

}

③關於高斯消元基礎(noip不會考於t1t2){

  這個模板是用來解決n階定方程的

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #define maxn 100
 6 using namespace std;
 7 int n,m,k,l,a,b,c;double mp[maxn][maxn];
 8 int main(){
 9     cin >> n;
10     for(int i = 1; i <= n; i ++)
11         for(int j = 1; j <= n + 1; j ++)
12             cin >> mp[i][j];   
13     for(int j = 1; j <= n; j ++){
14         int rgt = 0;
15         for(int i = j; i <= n; i ++)
16             if(mp[i][j]){rgt = i; break;}
17         if(!rgt)continue;
18         if(rgt ^ j)swap(mp[rgt],mp[j]);
19         
20         for(int i = j + 1; i <= n; i ++){
21             double div = mp[i][j] / mp[j][j];
22             for(int k = 1; k <= n + 1; k ++)mp[i][k] -= div*mp[j][k];
23         }
24     }
25     for(int j = n; j >= 1; j --){
26         if(mp[j][j] == 0){cout<<"No Solution"; return 0;}
27         mp[j][n+1] =  mp[j][n+1] / mp[j][j];
28         for(int i = j-1; i >= 1; i --)mp[i][n+1] -= mp[j][n+1] * mp[i][j];
29     }
30     for(int i = 1; i <= n; i ++)printf("%.2lf
" ,mp[i][n+1]);
31     return 0;
32 }

高斯消元還可以解決一些異或,異或這個東西跟加法很相似。

因為若 a+(1<<k)&&(a>>k)&1==0 這個時候更加一樣,否則就是減。

我們發現高斯消元是on^3的,那麼如果讓跑1000之內的怎麼辦呢。

我們就需要一個玄學的東西叫做線型基!

這個東西是一個純粹的省選演算法,noip向瞭解即可。

}

④基本數列公式

{

1.等比數列求和

2.等差數列求和

3.等差數列性質

題目

1.P3938 斐波那契

2.P1306 斐波那契公約數

3.P1962 斐波那契數列

4.P1939 【模板】矩陣加速(數列)

5.T37388 P哥破解密碼

 4.函式(積性函式初步)

①三分法{

用於求單峰函式的極值

我們發現如果取兩個點(三分之一位置)mid1 mid2

會有三種情況(還有一種是相等,那時候任意都行)

mid1>mid2那麼峰值不可能在mid2-r;

mid1<mid2峰值不可能在 l-mid1;

那麼我們就會發現遞推即可縮小lr

鎖定峰值

洛谷搜尋三分法有模板

 1 // luogu-judger-enable-o2
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<iostream>
 7 #include<queue>
 8 #define maxn 1000
 9 using namespace std;
10 double n,m,a,b,mid1,mid2,k[maxn];
11 double f(double x){
12     double ans;
13     ans=k[0];
14     for(int i=1;i<=n;i++){
15         ans+=pow(x,i)*k[i];    
16     }
17     return ans;
18 }
19 double sanfen(double l,double r){
20     if(r-l<0.000001)return l;
21     double mid1=(r-l)/3+l;
22     double mid2=r-(r-l)/3;
23     //cout<<f(mid1)<<" "<<f(mid2)<<endl;
24     if(f(mid1)>f(mid2))r=mid2;
25     else l=mid1;
26     return sanfen(l,r);
27 } 
28 int main(){
29     cin>>n>>a>>b;
30     
31     for(int i=n;i>=0;i--){
32         cin>>k[i];
33     }//cout<<f(a)<<" "<<f(b)<<endl; 
34     printf("%.5lf",sanfen(a,b));
35     return 0;
36 }

這個東西還可以處理凸殼(見計算幾何)

}

② 尤拉函式

我在前面寫了篩法,自己回去找吧

有用的性質(莫比烏斯反演證明出來的)(Σ(d|n)φ(d))=n

中間的豎槓,整除的意思。

③莫比烏斯反演初步——這個noip如果考了 ccf就是瘋了,真的!!!!!

做題可以看一個叫popoqqq的人的部落格最底下。

5.概率論

考過 不難 沒啥好總結的 主要是推式子 那就結束吧

省選數學有哪些內容呢? 

哎,加油吧!

 

 

 

 

一維數學
exgcd,crt,excrt,gcd,bsgs,exbsgs,快速冪,龜速乘,雜湊,MillerRabin,Ruben rho,線性篩積性函式,素數線性篩,尤拉公式,費馬小定理,乘法逆元,尤拉函式優化快速冪,拉格朗日查值,莫比烏斯反演,fft/ntt,杜教篩,高斯函式化簡,牛頓迭代,泰勒展開。
計算幾何
凸包,半平面交,旋轉卡殼,三分法,導數,定積分分析時間複雜度
組合數學(二維數學)
盧卡斯定理,擴充盧卡斯,matrixtree,高斯消元,異或方程,線性基,行列式求值,矩陣乘法,矩乘快速冪,遞推數列特徵根

相關文章