数学 知识

一、质数(素数)

欧拉筛:

每个数只被筛一次,可以避免重复删
a 记录是否为质数,b存储全部质数

y=0;
for(int i=2;i<=q;i++)
{
    if(a[i]==0) b[y++]=i;
    for(int j=0;b[j]*i<=q;j++) //质数的i倍
    {
        a[b[j]*i]=1; //不是质数
        if(i%b[j]==0) break; //及时截至
    }
}

二、组合数(Cnm)

1、杨辉三角:

for(int i=0;i<=40;i++)
{
    for(int j=0;j<=i;j++)
    {
        if(j==0) a[i][j]=1;
        else a[i][j]=a[i-1][j-1]+a[i-1][j]; //Cmn = Cmn−1 + Cm−1n−1
    }
}
还有他的兄弟,计算Amn
sum=1;
m=n-m+1;
for(int i=m;i<=n;i++)
{
    sum*=i;
    sum%=p;
}

2、lucas 定理:
对于质数 p 以及正整数 m > n 在 p 进制 下:
m = a0 + a1 * p + …… + ak * p ^ k
n = b0 + b1 * p + …… + bk * p ^ k
则有:
请添加图片描述

ull qmi(ull a, ull b, int p)//快速幂
{
    ull x=1;
    while (b)
    {
        if (b & 1) x = (ull)x * a % p;
        a = (ull)a * a % p;
        b>>=1;
    }
    return x;
}
int C(int a, int b, int p)  // 通过定理求组合数C(a, b)
{
    if (a < b) return 0;
    ull x = 1, y = 1;  // x是分子,y是分母
    for (int i = a, j = 1; j <= b; i --, j ++ )
    {
        x = x * i % p;
        y = y * j % p;
    }
    return x * qmi(y, p - 2, p) % p;用到费马小定理
}

ull lucas(ull a, ull b, int p)
{
    if (a < p && b < p) return C(a, b, p);
    return (ull)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}

三、约数

1、每个数都是由几个质数(2,3,5,7,11,13,17,19,21,23 ……)相乘得到的
例:20 = (2 ^ 2) *( 5 ^ 1)

2、 约数个数:
假设有一个数N = 2 ^ x * 3 ^ y * 4 ^ z * ……
即 共有 x 个 2 ,y 个3 ……
每次可选0,1,2,,,x 个 2 ,同理也可选 0 - y 个3 …… (每种不同的选法都对应一个约数)

所有的约数个数是:(x+1) (y+1)(z+1)
约数之和:(2 ^ 0 + 2 ^ 1 + 2 ^ 2 ……+2 ^ x)( 3 ^ 0 + 3 ^ 1 + 3 ^ 2 ……+3 ^ y)(4 ^ 0 + 4 ^ 1 + 4 ^ 2 ……+ ^ z)……

n个学生中挑出k个人使得能力值的最大公约数最大//计算能整除自身的个数
每头奶牛轮流走上一圈,找到手上数字能整除在自己纸条上的数字的牛的头的个数,//计算自身能整除的个数
都是先求出最大数,并且存当前这个数共有几个
循环 i ,求出能整除 1-max 的个数(即求能把 i 作为因子的所有个数)

for(int i=1;i<=n;i++)
{
    cin>>x;
    b[x]++;
    m=max(m,x);
}
for(int i=1;i<=m;i++)
{
    //自身能整除
    if(b[i])
    {
        for(int j=1;j*i<=m;j++)
        {
            c[j*i]+=b[i];
        }
    }
    // 能整除自身
    for(int j=1;j*i<=m;j++) //能整除i的,统计个数
    {
        sum+=b[j*i];
    }
}

四、模运算

商 * 除数+余数=被除数
(a+b) % m = ( (a % m ) + (b % m) ) % m;
(a - b) % m = ( (a % m ) - (b % m) ) % m; // (a - b) % m = ( (a % m ) - (b % m) + m ) % m //保证结果为正数
(a * b) % m = ( (a % m ) * (b % m) ) % m;
没有除法;

负进制数:
为了保证余数不出现负数 : 余数-除数(减去负的为正),商+1
(商+1) * 除数 +(余数-除 数) < = > 商 * 除数 + 余数
举例: -15 = 7 * (-2)+ -1 // -15是7个-2 余 -1
让余数减去一个 -2 ,即余 1 时,就要变成 8 个 -2 ,才能让式子相等

五、快速幂

当求 a^n 的 n 比较大时
原理:a ^ n +a ^ m =a ^ m+n

快速幂 + 快速幂取模

while(b>0)
{
	if(b%2==1)//如果是奇数就多乘一次
	{
	    x *= a;
	    //x %= p;
	}
	a*=a;
	//a%=p;
	b=b/2;最后一定会变成 1
}
//y=x%p;

六、最大公约数 , 最小公倍数

1、GCD、LCM

1、GCD :
性质:
①、可以用来约分
②、gcd (a,0) = a ;

  1. 公式: __gcd(x,y)
  2. 辗转相除法
    c=a%b;
    while(c!=0)
    {
    	a=b;
    	b=c;
    	c=a%b;	
    }
    cout<<b<<endl;
    

2、LCM

x = a / gcd(a,b) * b;
2、拓展欧几里得

a* x + b* y = gcd(a,b) 求解 x0,y0
原始简化得 : c * x + d *y =1 //c = a / (gcd(a,b)) , d = b / (gcd(a,b)) , c d互质
然后得通解 :
x = x0 + d * t
y = y0 - c * t // t 为任意数

void extend_gcd(int a,int b)
{
    if(b==0)
    {
        x=1,y=0;
        return;//x=1,y=0,a∗1+b∗0=gcd(a,b)此时的a就是gcd(a,b)
    }
    extend_gcd(b,a%b);
    //此时的x和y已经是回溯后的了
    int temp = x;
    x = y;
    y = temp -(a/b)*y;
}
int main()
{
    cin>>a>>b;
    x=0; y=0;
    extend_gcd(a,b);
    return 0;
}

七、位运算

1、与1异或,可以使特定位翻转 ,与0异或,保留其值 0101 0101 ^ 1111 0000 = 1010(翻转) 0101(保留)

2、1除了最低位,其他位都为0,所以按位与结果取决于n最后一位,如果n最后一位是1,则结果为1,反之结果为0
通常用来判断当前位是否为 1

3、0的二进制是: 00000000……… -1的二进制是:1111111111…………
只用0和-1进行位运算,就可以得到任何一位的任何情况进行位运算的结果

4、k 个相同的数的异或和,当 k 为奇数时,结果是这个数本身,否则结果是 0 。

八、求解逆元

前面的模运算说过有加减乘的模运算,但是除法却不符合,逆元就是解决此类问题
即,用于求 ( a / b ) mod p
所以对于( a / b )mod p ,我们就可以求出 b 在 mod p 意义下的逆元,然后乘上 a ,再 mod p ,就是这个乘法逆元的值了

1、拓展欧几里得
x=0;y=0;
extend_gcd(i,b);
x=(x%b+b)%b;
2、快速幂+费马小定理

费马小定理: 如果 a,p 互质,那么 a ^(p-1) ≡ 1 ( m od p )

结合逆元方程 :a ∗ x ≡ 1 ( mod p ) ,得到 a ∗ x ≡ a ^(p−1) * (mod p)
根据同余的性质 ,若 p 为质数,得到 x ≡ a ^ ( p - 2 ) * (mod p) , 然后快速幂 求解即可

scanf("%lld%lld",&n,&b);
x=b-2; y=1;
a;
while(x>0)
{
    if(x%2==1)//如果是奇数就多乘一次
    {
        y *= a; y %= b;
    }
    a*=a; a%=b;
    x=x/2;
}
y=y%b;
printf("%lld\n",y);
3、线性算法 (复杂度 O(n))

如果要解模的逆元的数 , 数量很多,但是连续 , 就可以使用递推法 。

scanf("%lld%lld",&n,&b); //当时的题时间卡的死
a[0]=0;
a[1]=1;
printf("1\n");
for(i=2;i<=n;i++)
{
    a[i]= (long long)(b-b/i)*a[b%i]%b;
    printf("%lld\n",a[i]);
}

九、特殊计数(递推公式)

1、斐波那契数列 :从第三项开始,每一项都等于前两项之和

f ( 1 ) = f ( 2 ) = 1
f ( n ) = f ( n - 1 ) + f ( n - 2 )

2、卡特兰数 C2n n / (n+1)

卡特兰数:1,1,2,5,14,42,132,429……
用来计算是否合法等问题
请添加图片描述

①:棋盘问题:一直在原对角线右下方,不穿过主对角线,路径数有多少
②:进栈出栈,有进才有出,进的一定比出的多,就是合法的进栈出栈总操作个数
③:括号问题:一串字符串只有" )" “(” 组成,找合法组合,共有多少组合方式
④:二叉树:n个结点能构成多少种二叉树
⑤:一个凸多边形能化成多少个三角形

3、斯特林数

1、第一类斯特林数:把n个不同元素分配到 k 个圆排列里,圆不能为空,有多少种分法:
s ( n , k ) = s ( n - 1 , k - 1 ) + ( n -1 ) * s ( n - 1 , k ) ,
s ( 0 , 0 ) = 1 , s ( k , 0 ) = 0
s ( n , n ) = 1 , s( n , 1 ) = ( n - 1 ) !

2、第二类斯特林数:把 n个不同的球分配到 k个相同的盒子里,不能有空盒子,有多少种分法:
s ( n , k ) = s ( n - 1 , k - 1 ) + k * s ( n - 1 , k ) ,
s ( 0 , 0 ) = 1 , s ( i , 0 ) = 0

例 2.1 :要将 n 个互不相同的球放入 k 个互不相同的盒子中,且不允许有空盒子, 有多少种不同的分法:

cin>>n>>m;
for(int i=0;i<=n;i++) //初始化
{
    f[i][0]=0;
}
f[0][0]=1;
for(int i=1;i<=n;i++)
{
    for(int j=1;j<=i;j++)
    {
        f[i][j]=j*f[i-1][j]+f[i-1][j-1];
    }
}
sum=1;
for(int i=1;i<=m;i++) //因为互不相同,所以要乘上Amm
{
    sum*=i;
}
cout<<f[n][m]*sum<<endl;

十、错排

排列n个数,每个数都不在自己原本的位置上

已知 D1 = 0 , D2​ = 1。
当 n=3 时:
设数 1 放在位置 2,那么,对于数字 2,它有两种情况:
①:数字 2 放到位置 1,那么就相当于 3 到 i 的排列中 m=0 的排列数,显然有D i−2 种排列数。
②:数字 2 不放到位置 1,那么就相当于 2 到 i 的排列中 m=0 的排列数,显然有 D i−1 种排列数。

递推公式:
d [ 1 ]=0 ,d[2] = 1
d [ i ] = ( i - 1 ) * ( d [ i - 1 ] + d [ i - 2 ] ) (i>=3)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值