NOIP数学复习

来源:互联网 发布:嵌入式linux应用开发 编辑:程序博客网 时间:2024/05/12 12:44

NOIP中的数学相关知识

素数及其相关

a.判断素数 复杂度O(根号n)

bool prime(int x) {    if(x == 0 || x == 1) return false;    for(int i = 2; i * i <= x; i++) {        if(x % i == 0) return false;    }    return true;

b.筛素数

埃氏筛法 复杂度O(n loglog n)

对于每个数p,会划掉p/n个数

#include<iostream>#include<cstdio>using namespace std;const int SIZE=1e7;int prime[SIZE];// 第i个素数bool is_prime[SIZE];//true表示i是素数int slove(int n){    int p = 0;    for(int i = 0; i <= n; i++)        is_prime[i] = true;//初始化    is_prime[0] = is_prime[1] = false;//0,1不是素数    for(int i = 2; i <= n; i++)    {        if(is_prime[i])//zkzk        {            prime[p++] = i;//计算素数的个数,也记录下了素数            for(int j = 2 * i; j <= n; j += i)// 除掉了i的倍数的数字                is_prime[j] = false;        }    }    return p;}int main(){    int n;    while(cin >> n)    {        int res = slove(n);        cout << res << endl;//素数个数         for(int i = 0; i < res; i++)//列出素数             cout << prime[i] << endl;    }}//zk:初始最小素数是2,将数据内2的所有倍数扔掉。此时3不能被更小的数整出,即为素数依次类推;

欧拉筛法 复杂度O(n)

luogu 线筛模板:

#include <cstdio>using namespace std;const int maxn=10000000;int n,m;int prime[maxn],flag[maxn];int tot;void get_prime(int n){    flag[0]=flag[1]=1;//不是素数     for (int i=2; i<=n; ++i)    {        if (!flag[i]) prime[++tot]=i;        for (int j=1; j<=tot&&i*prime[j]<=n; ++j)        {            flag[i*prime[j]]=1;            if (i%prime[j]==0) break;//prime[j]必定是prime[j]*i的最小因子;i中有因子prime[j],已被筛到一次 就退出找下一个i         }    }}int main(){    scanf("%d%d", &n, &m);    get_prime(n);    int t;    for (int i=1; i<=m; ++i)    {        scanf("%d", &t);        if (!flag[t]) printf("Yes\n");        else printf("No\n");    }    return 0;}

2.区间筛素数

(poj2689)
筛[a,b)中的素数
因为b以内合数的最小质因数一定不超过sqrt(b),先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数。
用埃氏筛的原理:

#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<cstring>using namespace std;typedef long long ll;const ll maxn=1e6+10;bool is_prime[maxn],is_p[maxn];//is_prime[]表示le~ri中下边位移后的素数判定(a位移1),is_p[]表示1~根号n中的素数判定 ll prime[maxn],sum;void get_prime(ll le,ll ri){    for(ll i=2;i*i<ri;i++) is_p[i]=true;//对[2,sqrt(b))的初始化全为质数,i要从2开始, 1不是素数     for(ll i=1;i<=ri-le;i++)    is_prime[i]=true;//对下标偏移后的[a,b)进行初始化,i要从1开始     for(ll i=2;i*i<=ri;i++){        if(is_p[i])for(ll j=2*i;j*j<=ri;j+=i) is_p[j]=false;//筛选素数i的倍数         for(ll j=max(2LL,(le+i-1)/i) * i;j<=ri;j+=i) is_prime[j-le]=false;//(a+i-1)/i得到最接近a的i的倍数,最低是i的2倍,然后筛选    }    for(ll i=1;i<ri-le;i++) if(is_prime[i]) prime[++sum]=i+le;}int main(){    ll l,r;    while(scanf("%lld%lld",&l,&r)){        sum=0;  memset(prime,0,sizeof(prime));        get_prime(l,r);        printf("%lld\n",sum);    }    return 0;}

3.分解质因数

基于唯一分解定理
n = P1^a1 * P2^a2 * …………* Pn^an(P1 < P2 < ……Pn),Pi为质数;
朴素法(可以先求出素数来优化一下)

void fj(int x) {    for(int i = 2; i * i <= x && x > 1; i++) {        while(x % i == 0) {            if(!check[i]) zhi[++cnt] = i;//check判断有没有出现过            check[i]++;            x /= i;            if(x == 1) break;        }    }}

例题QwQ、Problem A. 最佳进制

快速幂

// a ^ b % modint ksm(int a,int b,int mod) {    int ans=1;    a%=mod    while(b){        if(b&1)ans=(ans*a)%mod;        b>>=1;        a=(a*a)%mod    }    return ans;}

排列组合

这里写图片描述
这里写图片描述

组合

void init(){    f[0][0]=1;    for(int i = 1; i <= inf; ++i){        f[i][0]=1;        for(int j = 1;j <= i; ++j){            f[i][j] = f[i-1][j] + f[i-1][j-1];        }    }}
next_permutation(a +1,a+1+n); STL全排列

同余

同余的性质

这里写图片描述

0.0

这里写图片描述
同余方程

欧几里得算法

好久之前的笔记——关于gcd,exgcd,cal

/*设两数为a、b(a>b),求a和b最大公约数(a,b)的步骤如下:
用a除以b,得a÷b=q……r1(0≤r1)。
若r1=0,则(a,b)=b;若r1≠0,则再用b除以r1,得b÷r1=q……r2 (0≤r2).
若r2=0,则(a,b)=r1,若r2≠0,则继续用r1除以r2,……
如此下去,直到能整除为止。其最后一个非零除数即为(a,b)。*/
//—————————————————————————————————-

#include<stdio.h>#define ll long long ll gcd(ll a,ll b){    return b==0?a:gcd(b,a%b);}int main(){    ll a,b;    while(scanf("%lld%lld",&a,&b)!=EOF)    {        printf("%lld\n",gcd(a,b));//事实上如果 a 小于 b,那第一次就会先交换 a 与 b。    }    return 0;}//--------------------------------------------------------void exgcd(ll a,ll b,ll& d,ll& x,ll& y){    if(!b){d=a;x=1;y=0;}    else {exgcd(b,a%b,d,y,x);y-=x*(a/b);}}

//逆元

ll cal(ll a,ll m){    ll d,x,y;    exgcd(a,m,d,x,y);    return (x%m+m)%m;} int main(){    ll a,b,d,x,y;    while(scanf("%lld%lld",&a,&b)!=EOF){        exgcd(a,b,d,x,y);        printf("%lld*%lld+%lld*%lld=%lld\n",a,x,b,y,d);    }    return 0;}

/*
同余方程
先讲一下扩展欧几里德定律:

对于不完全为0的非负整数a,b,gcd(a, b)表示a, b的最大公约数,必定存在整数对x,y,满足a*x+b*y==gcd(a, b)。

证明:(转)

a*x1+b*y1=gcd(a, b);

b*x2+(a%b)*y2=gcd(b, a%b);

因为由欧几里德定理知:gcd(a, b)==gcd(b, a%b)

所以a*x1+b*y1=b*x2+(a%b)*y2; 因为r=a%b, r =a-k*b所以==>

a*x1+b*y1=b*x2+(a-k*b)*y2; 因为k=a/b;所以==>

a*x1+b*y1=b*x2+(a-(a/b)*b)*y2; 展开得到==>

a*x1+b*y1=b*x2+a*y2-b*(a/b)*y2;转换得到 ==>

a*x1+b*y1=a*y2+b*(x2-(a/b)*y2);

观察上式可知 x1=y2, y1=x2-a/b*y2;

由此可知x1,y1是由x2,y2得出来的,由此类推x2,y2是由x3,y3得出来的,

那什么时候是终止呢?也就是递归gcd(a, b)中b=0时;也就是说此时a的值就是要求得最大公约数

即gcd(a, 0)此时由扩展欧几里得定律a*x+b*y==gcd(a, b)

知 a*x+b*y=a;

解出x=1, y=0;

此时就是递归终止的地方:

——————–分界线————————

那么问题来了,这破东西有啥用呢

问的好,它可以用来求一个同余方程的解,也就是逆元

ax ≡ 1 (mod b),现在找x能够使这个式子成立

这个式子等价于ax+by=1

这不就是拓展欧几里得的表达式吗,从这里我们可以看出如果gcd(a,b)!=1那么这个方程就不会有解

所以说呢

形如a*x + b*y = c这样的式子,若c%gcd(a,b)==1,那么这个方程就会有解

PS:题目保证有解,所以不用判断了

但是一般题目里会让你求一个最小的x,当你用拓欧求出一个解时,一般会让你去找一个最小解,我们只需要对这个数取模b就行了(如果求正数,你只需要先加一个b,再取模行了,应该都知道吧)

代码如下

#include <cstdio>using namespace std;typedef ll ll;ll a,b,x,y;ll e_gcd(ll a,ll b,ll &x,ll &y){    if(!b)    {        x=1;        y=0;        return a;    }    ll ans=e_gcd(b,a%b,x,y);    ll tmp=x;    x=y;    y=tmp-(a/b)*y;    return ans;} int main(){    scanf("%lld%lld",&a,&b);    e_gcd(a,b,x,y);    printf("%lld",(x+b)%b);    return 0;}

1.最大公因数

int gcd(int a,int b) {    if(!b) return a;    else return gcd(b, a % b);}

2.最小公倍数

gcd(a,b) * lcm(a,b) = a * b; lcm(a,b) = a * b / gcd(a,b);int lcm(int a,int b) {    return a * b / gcd(a,b);}

霍dalao的丑字:
这里写图片描述

扩展欧几里得算法

void exgcd(int a, int b, int &d, int &x, int &y) {    if(!b) {d = a, x = 1, y = 0, return;}    exgcd(b, a % b, d , y, x);     y -= x * (a / b);}

扩欧求解不定方程:
这里写图片描述

逆元

1.扩欧

这里写图片描述

int inv(int a, int n) {    int d,x,y;    exgcd(a,n,d,x,y);    return (x % n + n) % n;}

2.费马小定理

a^(p-1) ≡1 (mod p)

int inv(int a, int n) {    return ksm(a,n-2,n)}

递推规律

1.Catalan数

这里写图片描述

h(n)=(4n-2)/(n+1)*h(n-1)(n>1) h(0)=1 h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)h(0) (n>=2) h(n)=C(2n,n)/(n+1)

2.错排公式

十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
f[1] = 0, f[2] = 1
f[i]=(i-1)*(f[n-1]+f[n-2])

3.斐波那契数列

f[1] = 1, f[2] = 1;
f[i] = f[i - 1] + f[i - 2]

5.除数函数

设 d(n)为 n 的所有因数的个数,由乘法原理可知,
d(n)=(a1 +1)(a2 +1)…(ak +1)。

4.秦九韶算法

这里写图片描述

int qing(int x){    int ans=a[n];    for(int i=n-1;i>=1;i--)        ans=ans*x+a[i];    return ans;}
原创粉丝点击