ACM组合数学模板

排列:

不可重复排列:A_{n}^{r} = n(n-1)(n-2)...(n-r+1) = \frac{n!}{(n-r)!}

可重复排列:从n个取可重复k个排列数为:n^{k}

圆排列:P_{n}^{m}=\frac{n!}{(n-m)!\times m},(1\leqslant m\leqslant n)

错位排列:D_n=n!(\frac{1}{2!}-\frac{1}{3!}+...+(-1)^n\frac{1}{n!})=[\frac{n!}{e}+0.5]

指数母函数定义:G(x) = a_{0}+\frac{a_{1}}{1!}x+\frac{a_{2}}{2!}x^{2}+...+\frac{a_{n}}{n!}x^{n}

组合:

不可重复组合:C_{n}^{r}=\frac{n!}{r!(n-r)!}

可重复组合:H_n^{r}=C_{n+r-1}^{r}=\frac{(n+r-1)!}{r!(n-1)!}

不相邻组合:从n个取m个不相邻组合数为:C_{n-m+1}^m

组合常用公式:C_n^{m+1}=\frac{n-m}{m+1}\times C_n^m

帕斯卡恒等式:C_{n+1}^{k} = C_{n}^{k-1} + C_{n}^{k}

普通母函数定义:G(x) = a_{0}x+a_{1}x^{1}+a_{2}x^{2}+...+a_{n}x^{n}

扫描二维码关注公众号,回复: 6082528 查看本文章

常见数列:

斐波那契数列:a_{n} = \frac{1 }{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^{n}-(\frac{1-\sqrt{5}}{2})^{n}]

卡特兰数列:

      递归公式1:f(n)=\sum _{i=0}^{n-1}f(i)\times f(n-i-1)

      递归公式2:f(n)=\frac{f(n-1)\times (4n-2)}{n+1}

      组合公式1:f(n)=\frac{C_{2n}^n}{n+1}

      组合公式2:f(n)=C_{2n}^n-C_{2n}^{n-1}

排列/组合数取模

ll fac[maxn],inv[maxn];
ll pow_mod(ll a, ll n) {
    ll res = 1;
    while(n) {
        if(n&1) res = res*a % mod;
          a = a*a % mod;
          n >>= 1;
    }
    return res;
}
void init() {
    fac[0] = 1;
    for(int i=1; i < maxn; i++) {
        fac[i] = fac[i-1]*i % mod;
    }
}
ll C(ll n, ll r) {
    return fac[n] * pow_mod(fac[r]*fac[n-r]%mod, mod-2) % mod;
    return fac[n] * pow_mod(fac[n-r], mod-2) % mod; // 排列数取模
}

错排问题

/*------------------------------------------------------------
    问题: 十本不同的书放在书架上。
    现重新摆放,使每本书都不在原来放的位置。有几种摆法?
    这个问题推广一下,就是错排问题,是组合数学中的问题之一。
    考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,
    那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。 
    研究一个排列错排个数的问题,叫做错排问题或称为更列问题。
------------------------------------------------------------*/
// dp[1]=0, dp[2]=1
// dp[i] = (i - 1)*(dp[i - 1] + dp[i - 2]); i > 2 
ll D(int n) {
    ll a = 0, b = 1, c;
    if(n < 3) return n-1;
    for (int i = 3; i <= n; i++) {
        c = ((i - 1) * 1ll * (a + b)) % mod;
        a = b;
        b = c;
    }
    return c;
}

卡特兰数列

/*--------------------------------------------------
  常见用法:
    卡特兰数的应用都可以归结到一种情况:有两种操作,
    分别为操作一和操作二,它们的操作次数相同,
    都为 N,且在进行第 K 次操作二前必须先进行至少 K 次操作一,
    问有多少中情况?结果就Catalan(N)。
    1.给定n个数,有多少种出栈序列
    2.n个结点的二叉树有多少种构型
    3.在nn的格子中,只在下三角行走,每次横或竖走一格,有多少种走法
    4.将一个凸n+2边型剖分成三角形的方法数
    5.在圆上选择2n个点,将这些点成对相连使得到的n条线段不相交的方法数
    6.n个长方形填充一个高度为n的阶梯状图形的方法数
    7.括号化问题,左括号和右括号各有n个时,合法的括号表达式的种类
    8.有n+1个数连乘,乘法顺序的种类
    9.n位二进制中,有m个0,其余为1,有h[C(n,m)-C(n,m-1)]种
--------------------------------------------------*/
ll fat[N];
ll inv[N];
ll finv[N];
void Init(){
    for(ll i=2,inv[1]=1; i < N; i++) 
        inv[i] = ((mod- mod/i) * 1ll * inv[mod%i])%mod;
    for(ll i=1,fat[0]=1,finv[0]=1; i < N; i++){
        fat[i] = (fat[i-1] * i) % mod;
        finv[i] = (finv[i-1]*inv[i]) % mod;
    }
}
ll C(ll n,ll m){
    ll res = 1;
    res = res*fat[n] % mod;
    res = (res * finv[m]%mod * finv[n-m] % mod);
    return res;
}
ll Ctl(ll n){
    return (C(2*n,n)-C(2*n,n-1)+mod)%mod;
}

普通母函数

/*--------------------------------------------------
    经典例题:有质量为1、2、4的砝码分别为1、3、2枚
      问:可以称出多少不同质量?要称出质量为3有几种方式?
      解:构造函数G(x)=(1+x)*(1+x^2+x^4+x^6)*(1+x^4+x^8)
      其中第二个括号中X^6表示:用第二个物品质量3枚,以此类推
    代码为模拟构造函数计算过程 比如:
      (1+x) * (1+x^2+x^4+x^6) * (1+x^4+x^8)
      = (1+x^2+x^4+x^6 + x+x^3+x^5+x^7) * (1+x^4+x^8)
      = ...
--------------------------------------------------*/
// v[i]第i个未知量价值,s/e:物品的最大/最小个数
int v[MAX], s[MAX], e[MAX]; 
// a为最终结果,b为中间结果。
int a[MAX], b[MAX];
// P为可能出现的最大指数
int P; 
 
void mu(int n) {      // n为种类数
    memset(a,0,sizeof(a));
    a[0]=1;
    for(int i=1; i <= n; i++) { //循环每个因子
        memset(b, 0, sizeof(b));
        // j为第i种物品可能的数量,j*v[i]即第i个括号中第j项的系数
        for (int j=n1[i]; j<=n2[i] && j*v[i]<=P; j++) {
            // 计算对前i-1项相乘的结果的第k项系数
            for (int k=0; k+j*v[i] <= P; k++)
                b[k+j*v[i]] += a[k];
        }
        memcpy(a, b, sizeof(b)); // 将b数组存回a数组
    }
}

指数母函数

/*-------------------------------------------------- 
    经典例题:假设有多个元素,其中a1重复n1次,a2重复n2次,
      a3重复n3次。从中取r个排列,求其排列数。 
    构造函数G(x) = (1+x/1!+x^2/2!+...+x^n1/n1!) *
      (1+x/1!+...+x^n2/n2!) *(1+x/1!+...+x^n3/n3!)
    根据普通母函数不难理解其中每个值的意思
--------------------------------------------------*/
ll fac[N];  // fac[i]表示第i个阶乘值
int a[N];   //1~n每种的个数
double c1[N],c2[N];  // double
 
void cal(int n,int m) {         //有n种,取m个
    memset(c1, 0, sizeof(c1));
    memset(c2, 0, sizeof(c2));
 
    c1[0] = 1.0/fac[0];
    for(int i=1; i<=n; i++) {
        for(int j=0; j<=m; j++) {
            for(int k=0; k+j<=m && k<=a[i]; k++)
                c2[k+j] += c1[j]/fac[k];
        }
        for(int j=0; j<=m; j++) {
            c1[j] = c2[j];
            c2[j]=0;
        }
    }
}
ans=c1[m]*fac[m];           //取m个时的多重排列数

Polya计算

/*==================================================*\
  | c种颜色的珠子, 组成长为s的项链, 项链没有方向和起始位置;
\*==================================================*/ 
int gcd (int a, int b) { return b ? gcd(b,a%b) : a; } 
int main (void){  
    int c, s;  
    while (scanf("%d%d", &c, &s)==2) {   
        int k;   
        long long p[64]; p[0] = 1; // power of c   
        for (k=0 ; k<s ; k++) p[k+1] = p[k] * c;   
        // reflection part   
        long long count = s&1 ? s * p[s/2 + 1] : (s/2) * (p[s/2] + p[s/2 + 1]);   
        // rotation part   
        for (k=1 ; k<=s ; k++) count += p[gcd(k, s)];   
        count /= 2 * s;   
        cout << count << '\n';  
    }  
    return 0; 
}

快速傅里叶变换(FFT)

typedef complex<double> cd; //复数类的定义
const int maxl=2094153;     //nlogn的最大长度
const double PI=3.14159265358979;
cd a[maxl],b[maxl];   //用于储存变换的中间结果
int rev[maxl];        //用于储存二进制反转的结果
void getrev(int bit) {
    //高位决定二进制数的大小
    for(int i=0; i<(1<<bit); i++){
        rev[i] = (rev[i>>1]>>1)|((i&1)<<(bit-1));
    }//能保证(x>>1)<x,满足递推性质
}
void fft(cd* a,int n,int dft){//变换主要过程
    for(int i=0; i<n; i++){//按照二进制反转
        //保证只把前面的数和后面的数交换,(否则数组会被翻回来)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
    }
    for(int step=1; step<n; step<<=1){ //枚举步长的一半
        cd wn=exp(cd(0, dft*PI/step));//计算单位复根
        for(int j=0; j<n; j+=step<<1){ //对于每一块
            //!!每一块都是一个独立序列,都是以零次方位为起始的
            cd wnk(1,0);
            for(int k=j; k<j+step; k++){ //蝴蝶操作处理这一块
                cd x = a[k];
                cd y = wnk*a[k+step];
                a[k] = x+y;
                a[k+step] = x-y;
                wnk *= wn; //计算下一次的复根
            }
        }
    }
    if(dft==-1){ //如果是反变换,则要将序列除以n
        for(int i=0;i<n;i++)
            a[i]/=n;
    }
}
int output[maxl];
char s1[maxl], s2[maxl];

void getmuti() {    //计算高精度大数乘法,输入两个数a,b
    scanf("%s%s",s1,s2);    //读入两个数
    int l1 = strlen(s1),l2=strlen(s2); //就算"次数界"
    int bit=1, s=2;    //s表示分割之前整块的长度
    for(bit=1; (1<<bit)<l1+l2-1; bit++){
        s <<= 1; //找到第一个二的整数次幂使得其可以容纳这两个数的乘积
    }
    for(int i=0; i<l1; i++){ //第一个数装入a
        a[i] = (double)(s1[l1-i-1]-'0');
    }
    for(int i=0; i<l2; i++){ //第二个数装入b
        b[i] = (double)(s2[l2-i-1]-'0');
    }
    getrev(bit); fft(a,s,1); fft(b,s,1); //dft
    for(int i=0; i<s; i++) a[i]*=b[i];    //对应相乘
    fft(a, s, -1); //idft
    for(int i=0; i<s; i++){ //还原成十进制数
        output[i] += (int)(a[i].real()+0.5); //注意精度误差
        output[i+1] += output[i]/10;
        output[i] %= 10;
    }
    int i;
    for(i=l1+l2; !output[i]&&i>=0; i--); //去掉前导零
    if(i==-1) printf("0"); //特判长度为0的情况
    for(; i>=0; i--){ //输出这个十进制数
        printf("%d",output[i]);
    }
    putchar('\n');
}

void getpoly() {   //计算多项式乘法
    int n, m;
    scanf("%d%d",&n,&m);    //输入量多项式最高项次数
    //读入第一个多项式的系数(a0+a1*x+a2*x^2+a3*x^3+.....+an*x^n)
    for(int i=0; i<=n; i++) scanf("%lf",&a[i].real());  
    //读入第二个多项式的系数(b0+b1*x+b2*x^2+b3*x^3+.....+bn*x^n)
    for(int i=0; i<=m; i++) scanf("%lf",&b[i].real());   
    int bit=0, s=1;  //s表示分割之前整块的长度
    for(s=1; s<=n+m; s<<=1)
        bit++;
    getrev(bit); fft(a,s,1); fft(b,s,1);//dft
    for(int i=0; i<s; i++) a[i] *= b[i];//对应相乘
    fft(a, s, -1); //idft
    for(int i=0; i<=n+m; i++)
        //表示乘完多项式各项的系数,(a0+a1*x+a2*x^3...)
        printf("%d ",(int)(a[i].real()+0.5));   
}

int main() {
    getmuti();     //10*10=100
    getpoly();    //(1+2x)*(1+2x+x2)=1+4x+5x2+2x3
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42024195/article/details/89736208