(Day6)演算法復健運動for藍橋杯-常用數學
1. 歐幾里得演算法:輾轉相除法(求gcd)
int gcd(int a, int b)
{
return 0 == b ? a : gcd(b, a % b);
}
或者C++直接呼叫:
編譯沒過,說是不能是無符號數
unsigned int a=10,b=5;
cout<<__gcd(a,b);
2. 尤拉篩(求質數)
int cnt=0;
vis[0]=1;
vis[1]=1;
for(int i=2; i<=10000000; i++)
{
if(!vis[i])
{
prime[cnt++]=i;
}
for(int j=0;j<cnt&&(ll)i*(ll)prime[j]<=10000000;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
3. 大數快速冪
ll f(ll a,ll b,ll mod)
{
ll ans=1;
while(b)
{
if(b&1)
ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
4. 矩陣快速冪
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=10000;
struct mat
{
ll a[2][2];
};
mat mat_mul(mat x,mat y)//兩個矩陣相乘
{
mat res;//新的矩陣
memset(res.a,0,sizeof(res.a));
for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
for(int k=0; k<2; k++)
res.a[i][j]=(res.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;
return res;
}
void mat_pow(int n)
{
mat c,res;
c.a[0][0]=c.a[0][1]=c.a[1][0]=1;
c.a[1][1]=0;
memset(res.a,0,sizeof(res.a));
for(int i=0; i<2; i++) res.a[i][i]=1;//初始為E
while(n)//非常眼熟
{
if(n&1) res=mat_mul(res,c);
c=mat_mul(c,c);
n>>=1;
}
printf("%d\n",res.a[0][1]);
}
int main()
{
int n;
while(~scanf("%d",&n)&&n!=-1)
{
mat_pow(n);
}
return 0;
}
5. 逆元
a ∗ b ≡ 1 (mod p),那麼 a,b 互為模 p 意義下的逆元
費馬小定理:
∀a,n滿足gcd(a,n)==1,則a^φ(n)≡1(mod n)
當p是質數,且a不能被p整除時,有a^(p-1)≡1(mod p)
在計算模逆元素時,通常使用費馬小定理的一個特例,即對於素數p和整數a,如果a 不是 p 的倍數,則$a^{p-1} \equiv 1 \pmod{p} $。由於模逆元素的定義是乘以一個數得到1,因此我們可以將$a^{p-1}$視為a 的逆元素。
因此,當p是一個素數,且a不是p 的倍數時,我們有$ a^{p-1} \equiv 1 \pmod{p} $,這意味著a的逆元素是$a^{p-2}$。因此,我們可以使用$ a^{p-2} $來計算a的模逆元素。
這種方法在很多情況下是有效的,因為它只需要進行一次指數運算和一次模運算,相對於其他求逆元素的方法更為高效。
所以廢話少說,求逆元就是,a%mod*f(b,mod-2)%mod
6. 唯一分解定理
任何一1的自然數N,如果N不為質數,都可以唯一分解成有限個質數的乘積
$N=p^a_1p_2 ^a ...p_n^a$
那麼,我們由中學所學的知識所知N的正因子共有(1+a1)(1+a2)...(1+an)個
而這些因子(不只是質因數)的和為
$(1+p_1+p_12..p_1a)(1+p_2+p_22..p_2a)$...
7.補充
把字串格式的數字轉化為int數字的函式
char s[10];
int sum=atoi(s);
memcpy:可以複製陣列
int a[109];
int b[109];
memcpy(b,a,sizeof(a));