Algorithm9.3 组合数

[TOC]

组合数

基本的排列组合数

A(n, m)和C(n, m)

第二类Stirling数

数学意义:把n个数划分成k个集合

实际意义:将n个球分到k个相同的盒子

// 递归写法
ll stirling(int n, int k){
    if (k==n || k==1) return 1;
    else if (k==2) return pow(2, (n-1)*1.0)-1;
    else return stirling(n-1, k-1)+k*stirling(n-1, k);
}
// 非递归写法

第二类Stirling数的变形

数学意义:把n个数划分成k个不同的集合

实际意义:把n个球分到k个不同的盒子里

和第二类Stirling数的区别:就是划分出来的集合再进行容器的匹配,对于不同的集合划分到不同的容器中。匹配原则:Ann

ll stirling(int n, int k){
    if (k==n || k==1) return 1;
    else if (k==2) return pow(2, (n-1)*1.0)-1;
    else return stirling(n-1, k-1)+k*stirling(n-1, k);
}
// 排列数Ann
ll factorial(ll n) {
    ll fc = 1;
    for (ll i = 1; i <= n; ++i) fc *= i;
    return fc;
}

https://www.luogu.com.cn/problem/P1287

#include <iostream>
using namespace std;
typedef long long ll;
int a, b;
#include <cmath>

ll factorial(ll n) {
    ll fc = 1;
    for (ll i = 1; i <= n; ++i) fc *= i;
    return fc;
}

ll stirling(int i, int j)
{
    if ((j == i) || (j == 1)) return 1;
    else if (j == 2) return pow(2, (i - 1) * 1.0) - 1;
    else return stirling(i - 1, j - 1) + j * stirling(i - 1, j);
}

int main() {
    scanf("%d%d", &a, &b);
    printf("%lld\n", stirling(a, b)*factorial(b));
    return 0;
}

最大公约数[gcd]与最小公倍数[gld]乘积 == 这两个数的乘积

a*b = gcd(a, b)*gld()

最小公倍数[gld]

可以利用最大公约数倒推回去,也就是gld(a, b)=a*b/gcd(a, b)

质数

判断1~n中的所有质数

1、基础:O(n^2)复杂度,遍历,遍历的边界从3~sqrt(x)

bool isprime(int x){
    for (int i = 3; i <= sqrt(x); ++i) if (x%i == 0) return false;
    return true;
}

2、埃拉托斯特尼筛:O(nlogn)复杂度

3、线性筛:O(n)

prime是质数列表,isprime

#define maxn 10000010
int vis[maxn], prime[maxn], isprime[maxn];

void Prime()
{
    for(int i=2; i<=b; i++)
    {
        if(!vis[i]) prime[cnt++] = i, isprime[i] = 1;
        for(int j=0; j<cnt&&i*prime[j]<=b; j++)
        {
            vis[i*prime[j]] = i;
            if(i%prime[j]==0) break;
        }
    }
}
b = 100; // 设置筛的边界
LPrime();
for (int i = 5; i < 20; ++i) {
    printf("%d %d %d\n", i, prime[i], isprime[i]); // prime是按照顺序的素数存放进来, isprime是索引的数是否为质数
}
Posted on Feb 1, 2021