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 【模板】矩陣加速(數列)
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,高斯消元,異或方程,線性基,行列式求值,矩陣乘法,矩乘快速冪,遞推數列特徵根