1 素數的相關問題
1 不大於n的素數的個數約爲n/ln(n)個;
2 兩個數互爲素數的條件 gcd(a,b)=1;
gcd
int gcd(int a,int b)
{return b?gcd(b,a%b):a;}
判斷一個數是否是素數
bool jud(int n)
{ if(n==1) return false;
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
return false;
}
return true;}
求1-n之間的數有多少個素數。
bool ispirme[100000];
void eratos(int n)
{ int i,j;
isprime[0]=0;
isprime[1]=0;
for(int i=2;i<=n;i++)
isprime[i]=1;
for(int i=2;i*i<=n;i++)
if(isprime[i])
for(int j=i*i;j<=n;j=j+i)
isprime[j]=false;
}
每個數都能分解爲素數相乘的形式。
n=p1 r1p2r2 p3r3…;
p爲素數 r爲指數。
應用 判斷兩個大數是否相等可以將大數拆分,看看其分解開的素因數是否相等。(double不適合大數的表示,精度問題)
int k=0;
for(int i=2; i*i<=n;)
{
if(n%i==0)
{
p[k]=i;
r[k]=0;
while(n%i==0)
{
r[k]++;
n=n/i;
}
k++;
}
if(i==2)
i++;
else
i=i+2;
}
if(n!=1)
{
p[k]=n;
r[k]=1;
k++;
}
結合篩法優化素數分解
bool isprime[10000];
int minfacter[100000];
//表示每個數的最小素因數。
void eratos(int n)
{ int i,j;
isprime[1]=isprime[0]=0;
minfactor[0]=minfactor[1]=-1;
for(int i=2;i<=n;i++)
{isprime[i]=1;
minfactor[i]=i;
}
for(int i=2;i*i<=n;i++)
{if(isprime[i])
{for(int j=i*i;j<=n;j=j+i)
{isprmie[j]=false;
if(minfactor[j]==j)
minfactor[j]=i;
}
}
vector <int> factorint x)
{vector<int> res;
while(x>1)
{ ret.push_back(minfactor[x]);
x/=minfactor[x];
}
return res;
}
n=p r1 p2 r2 p3 r3…
n的正約數的個數爲(r1+1)(r2+1)…(rn+1)
r爲指數
n 的所有正約數的和爲(1+p1+p1^2+…p1r1)*…(1+pk+pk2+…pkrk);
求n的所有因數的和
#include <iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
long long power(long long base, long long power)
{
long long result = 1;
while (power > 0) {
if (power & 1) {
result = result * base;
}
power >>= 1;
base = (base * base);
}
return result;
}
ll sum(ll p,ll n)
{
if(n==0) return 1;
if(n%2) return (sum(p,n/2)*(1+power(p,n/2+1)));
else
{
return sum(p,n/2-1)*(1+power(p,n/2+1))+power(p,n/2);
}
}
int main()
{
int t;
scanf("%d",&t);
int p[10005]= {0},r[10005]= {0};
while(t--)
{
ll n;
scanf("%lld",&n);
ll tt=n;
int k=0;
for(int i=2; i*i<=n;)
{
if(n%i==0)
{
p[k]=i;
r[k]=0;
while(n%i==0)
{
r[k]++;
n=n/i;
}
k++;
}
if(i==2)
i++;
else
i=i+2;
}
if(n!=1)
{
p[k]=n;
r[k]=1;
k++;
}
ll ans=1;
for(int i=0; i<k; i++)
{
ans=ans*sum(p[i],r[i]);
}
printf("%lld\n",ans);
}
return 0;
}
最大公約數
gcd(a,b)=gcd(a,ka+b);
1 佔轉相除法
int gcd(int a,int b)
{return b?gcd(b,a%b):a;}
2 更相減損術
int gcd(int m,int n)
{if(m==n)return m;
if(m<n) return gcd(n,m);
if(m&1==0)
return (n&1==0)?2*gcd(m>>1,n>>1):gcd(m>>1,n);
return (n&1==0)?gcd(m,n>>1):gcd(n,m-n);
}
最小公倍數
lcm=ab/gcd(a,b);
lcm(ma,mb)=mlcm(a,b);
lcm(a,b,c)=lcm(lcm(a,b),c);
ans=1;
for(int i=1;i<=n;i++)
{cin>>a[i];
ans=ans*a[i]/gcd(ans,a[i]);
}
cout<<ans<<endl;
和%相關的問題
1 在只含有加法和乘法的式子中,如果最後的運算結果需要對p 取模,那麼可以在計算中隨時取模。(a+b)%m=(a%m+b%m)%m;
2 4%3=1,2%3=2,1-2=-1 ,(4%3-2%3+3)%3;(a-b)%m=(a%m-b%m+m)%m;
總結
數論的東西往往用來解決值很大數據量很多很耗時間的問題,採用的策略往往是分解爲數的相乘,取模,用小小區間映射大區間,用小數映射大的數,小數和大數的聯繫可以解決很多問題。一個數的素因子最大爲根號下該數,那麼數的範圍就會被大大降低,而且,素數的倍數可以計算出合數,那麼小的區間就可以管理大的區間。需要取模的問題每一步都取模,能避免中間的值所會出現的數據溢出的問題。數組下表與取模結合,可以使得環狀重複出現的值重複使用,(循環要從零開始)。