数学

    数学是算法的根源、学好数学才能在算法这条路上走的更远、

 

最基本的是求 GCD ( 最大公约数)

可以用递归和迭代的方式实现、一般为了代码简洁都会用递归实现、

代码如下、复杂度O(logmax(a,b))

int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}
View Code

 

接下来是素数相关的东西、素数在数学中的地位相当重要、有关素数的算法非常多、

首先我们判断一个数是不是素数、

素性测试、复杂度O(sqrt(n))

bool is_prime(int n) {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return n != 1; // 1是例外
}
View Code

 

然后我们枚举一个数的约数有哪些、复杂度O(sqrt(n))

vector<int> divisor(int n) {
    vector<int> res;
    for (int i = 1; i * i <= n; i++) {
        if (n % i ==0) {
            res.push_back(i);
            if (i != n / i) res.push_back(n / i);
        }
    }
}
View Code

 

接下来是素数打表、埃氏筛选法、复杂度O(nloglogn)

int prime[MAX_N];             // 第i个素数
bool is_prime[MAX_N + 1];     // is_prime[i]为true表示i是素数

// 返回n以内素数的个数
int sieve(int n) {
    int p = 0;
    for (int i = 0; i <= n; i++) is_prime[i] = true;
    is_prime[0] = is_prime[1] = false;
    for (int i = 2; i <= n; i++) {
        if (is_prime[i]) {
            prime[p++] = i;
            for (int j = 2 * i; j <= n; j += i) is_prime[j] = false;
        }
    }
    return p;
}
View Code

 

线性筛选法

// 素数筛选、存在小于等于MAXN的素数
// prime[0] 存的是素数的个数
const int MAXN = 10000;
int prime[MAXN + 1];
void GetPrime() {
    memset(prime, 0, sizeof (prime));
    for (int  i = 2; i <= MAXN; i++) {
        if (!prime[i]) prime[++prime[0]] = i;
        for (int j = 1; j <= prime[0] && prime[j] <= MAXN / i; j++) {
            prime[prime[j] * i] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}
View Code

 

欧拉函数

欧拉函数的值等于不超过m并且和m互素的数的个数
如果x和m互素 x^(elur(m)) = 1(mod m)

求欧拉函数值  复杂度O(sqrt(n))

int euler_phi(int n) {
    int res = n;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            res = res / i * (i - 1);
            for (; n % i == 0; n /= i);
        }
    }
    if (n != 1) res = res / n * (n - 1);
    return res;
}
View Code

O(MAX_N)时间筛出欧拉函数值的表

int euler[MAX_N];
void euler_phi2() {
    for (int i = 0; i < MAX_N; i++) euler[i] = i;
    for (int i = 2; i < MAX_N; i++) {
        if (euler[i] == i) {
            for (int j = i; j < MAX_N; j += i) euler[j] = euler[j] / i * (i - 1);
        }
    }
}
View Code

 

二分快速幂运算

复杂度 O(logn)

typedef long long ll;
ll mod_pow(ll x, ll y, ll mod) {
    ll res = 1;
    while (n > 0) {
        if (n & 1) res = res * x % mod; //如果二进制最低位为1、则乘上x^(2^i)
        x = x * x % mod;  // 将x平方
        n >>= 1;
    }
    return res;
}
View Code

 

矩阵快速幂运算

const int MOD = 1e9 + 7;
struct Matrix {
    long long mat[2][2];
};
Matrix mul(Matrix a, Matrix b) {
    Matrix ret;
    for(int i = 0; i < 2; i++) {
        for(int j = 0; j < 2; j++) {
            ret.mat[i][j] = 0;
            for(int k = 0; k < 2; k++) {
                ret.mat[i][j] += a.mat[i][k] * b.mat[k][j];
                ret.mat[i][j] %= (MOD - 1);
            }
        }
    }
    return ret;
}
Matrix pow_M(Matrix a,int n) {
    Matrix ret;
    memset(ret.mat, 0, sizeof(ret.mat));
    ret.mat[0][0] = ret.mat[1][1] = 1;
    Matrix temp = a;
    while (n) {
        if (n&1) ret = mul(ret, temp);
        temp = mul(temp, temp);
        n >>= 1;
    }
    return ret;
}
View Code

 

随机素性测试大数分解

给定一个64位整数,问是否为质数,如果不是,则输出其最小因子。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;

//****************************************************************
// Miller_Rabin 算法进行素数测试
//速度快,而且可以判断 <2^63的数
//****************************************************************
const int S=8;//随机算法判定次数,S越大,判错概率越小 S一般取8-20


//计算 (a*b)%c.   a,b都是long long的数,直接相乘可能溢出的
//  a,b,c <2^63
long long mult_mod(long long a,long long b,long long c)
{
    a%=c;
    b%=c;
    long long ret=0;
    while(b)
    {
        if(b&1){ret+=a;ret%=c;}
        a<<=1;
        if(a>=c)a%=c;
        b>>=1;
    }
    return ret;
}



//计算  x^n %c
long long pow_mod(long long x,long long n,long long mod)//x^n%c
{
    if(n==1)return x%mod;
    x%=mod;
    long long tmp=x;
    long long ret=1;
    while(n)
    {
        if(n&1) ret=mult_mod(ret,tmp,mod);
        tmp=mult_mod(tmp,tmp,mod);
        n>>=1;
    }
    return ret;
}





//以a为基,n-1=x*2^t      a^(n-1)=1(mod n)  验证n是不是合数
//一定是合数返回true,不一定返回false
bool check(long long a,long long n,long long x,long long t)
{
    long long ret=pow_mod(a,x,n);
    long long last=ret;
    for(int i=1;i<=t;i++)
    {
        ret=mult_mod(ret,ret,n);
        if(ret==1&&last!=1&&last!=n-1) return true;//合数
        last=ret;
    }
    if(ret!=1) return true;
    return false;
}

// Miller_Rabin()算法素数判定
//是素数返回true.(可能是伪素数,但概率极小)
//合数返回false;

bool Miller_Rabin(long long n)
{
    if(n<2)return false;
    if(n==2)return true;
    if((n&1)==0) return false;//偶数
    long long x=n-1;
    long long t=0;
    while((x&1)==0){x>>=1;t++;}
    for(int i=0;i<S;i++)
    {
        long long a=rand()%(n-1)+1;//rand()需要stdlib.h头文件
        if(check(a,n,x,t))
            return false;//合数
    }
    return true;
}


//************************************************
//pollard_rho 算法进行质因数分解
//************************************************
long long factor[100];//质因数分解结果(刚返回时是无序的)
int tol;//质因数的个数。数组小标从0开始

long long gcd(long long a,long long b)
{
    if(a==0)return 1;//???????
    if(a<0) return gcd(-a,b);
    while(b)
    {
        long long t=a%b;
        a=b;
        b=t;
    }
    return a;
}

long long Pollard_rho(long long x,long long c)
{
    long long i=1,k=2;
    long long x0=rand()%x;
    long long y=x0;
    while(1)
    {
        i++;
        x0=(mult_mod(x0,x0,x)+c)%x;
        long long d=gcd(y-x0,x);
        if(d!=1&&d!=x) return d;
        if(y==x0) return x;
        if(i==k){y=x0;k+=k;}
    }
}
//对n进行素因子分解
void findfac(long long n)
{
    if(Miller_Rabin(n))//素数
    {
        factor[tol++]=n;
        return;
    }
    long long p=n;
    while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);
    findfac(p);
    findfac(n/p);
}
int main()
{
   // srand(time(NULL));//需要time.h头文件  //POJ上G++要去掉这句话
    int T;
    long long n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d",&n);
        if(Miller_Rabin(n))
        {
            printf("Prime\n");
            continue;
        }
        tol=0;
        findfac(n);
        long long ans=factor[0];
        for(int i=1;i<tol;i++)
          if(factor[i]<ans)
             ans=factor[i];
        printf("%I64d\n",ans);
    }
    return 0;
}
View Code

 

 

 

数学中另一个比较基础的东西就是模运算、其中同余模定理是核心、

 

约瑟夫环问题

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
f[1]=0; f[i]=(f[i-1]+m)%i; (i>1) 下标从0开始
f[1]=1; f[i]=(f[i-1]+m)%i (i>1); if(f[i]==0) f[i]=i; 下标从1开始

 

线性同余方程

数学表达式为ai * x = bi(mid mi)(1 <= i <= n)
解的全集可以写成x = b(mod m) 问题就转化为了求b和m
// 返回一个(b, m)的数对
pair<int, int> linear_congruence(const vector<int>& A, const vector<int>& B, const vector<int>& M) {
    //由于最开始没有任何限制、所以先把解设为表示所有整数的x = 0(mod 1)
    int x = 0, m = 1;
    for (int i = 0; i < A.size(); i++) {
        int a = A[i] * m, b = B[i] - A[i] * x, d = gcd(M[i], a);
        if (b % d != 0) return make_pair(0, -1); // 无解
        int t = b / d * mod_inverse(a / d,M[i] / d) % (M[i] / d);
        x = x + m * t;
        m *= M[i] / d;
    }
    return make_pair(x % m, m);
}
View Code

 

中国剩余定理

假设同余方程组里所有的ai都等于1,并且所有的mi都互素,答案就一定是x = b(mod m1*m2*...*mi)
对于一个合数n,我们假设n=ab(其中a和b互素)
那么(x mod n) == (x mod a, x mod b)
换句话说,以合数n为模数来考虑与以a和b为模数来考虑是等价的
其中n不能被任何一个完全平方数整除

 

扩展GCD

ax + by = gcd(a, b) 恒成立
ax + by = 1  gcd(a, b)等于1时有解
int extgcd(int a, int b, int &x, int &y) {
    int d = a;
    if(b != 0) {
        d = extgcd(b, a % b, y, x);
        y -= (a / b) * x;
    } else {
        x = 1; y = 0;
    }
    return d;
}
View Code

 

求逆元

ax = 1(mod m)等价于存在整数k使得ax = 1 + mk
变形得到ax - mk = 1 如果gcd(a, m)!=1那么逆元不存在
int mod_inverse(int a, int m) {
    int x, y;
    extgcd(a, m, x, y);
    return (m + x % m) % m;
}
View Code

 

费马小定理

如果p是素数 x^p = x(mod p)
如果x无法被p整除、则x^(p - 1)= 1(mod p)
a^(-1) = a^(p - 2)(mod p) 可以通过快速幂求出逆元

 

组合数取模

如果预处理出1<=n<p范围中n!mod p 的表就可以在O(logp n)时间内算出答案
否则复杂度是O(plogp n)
// 求nCk mod p O(logp n)
int mod_comb(int n, int k, int p) {
    if (n < 0 || k < 0 || n < k) return 0;
    int e1, e2, e3;
    int a1 = mod_fact(n, p, e1);
    int a2 = mod_fact(k, p, e2);
    int a3 = mod_fact(n - k, p, e3);
    return a1 * mod_inverse(a2 * a3 % p, p) % p;
}
View Code

 

Lucas定理

A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。
则组合数C(A,B)与C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0]) modp同余
即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)

对于大组合数取模,n,m不大于10^5的话,用逆元的方法,可以解决。对于n,m大于10^5的话,那么要求p<10^5,这样就是Lucas定理了,将n,m转化到10^5以内解。

推广:奇偶性质(联系2进制分析) 0C1=1 0C0=1C0=1C1=1         注:14年上海网络赛B题也是一个Lucas定理的应用

 

long long Lucas(long long a, long long k, long long p) {
//求C(n,m)%p p最大为10^5。a,b可以很大!  
    long long re = 1;  
    while (a && k) {  
        long long aa = a % p;
        long long bb = k % p;  
        if (aa < bb) return 0; //C(aa,bb) 表示 在aa里面取bb个,取法为0
        //由于p是素数,所以 a / b % p ,b 对于 mod a 肯定存在逆元  
        re = re * fact[aa] * mod_pow(fact[bb] * fact[aa-bb] % p, p - 2, p) % p;
        //这儿的求逆不可先处理  
        a /= p;
        k /= p;
    }
    return re;
}  
View Code

 

N!%p

假设p是一个素数

int fact[MAX_P]; // 预处理的n! mod p 的表 O(p)
// 分解n!=a*p^e 返回a mod p  O(logp n)
int mod_fact(int n, int p, int& e) { 
    e = 0;
    if (n == 0) return 1;
    // 计算p的倍数的部分
    int res =mod_fact(n / p, p, e);
    e += n / p;
    // 由于(p-1)!=-1 因此(p-1)!^(n/p)只需要知道n/p的奇偶性就可以计算了
    if (n / p % 2 != 0) return res * (p - fact[n % p]) % p;
    return res * fact[n % p] % p;
}
View Code

 

 

组合数学也是数学中一个经典分支、个人感觉组合比数论更加有趣、

母函数

母函数需要掌握普通母函数和指数型母函数

普通型母函数主要是来求组合的方案数,而指数型母函数是求多重排列数。

学习资料:click here

 

置换及其运算

置换比较容易理解、普通的计算也只是找循环节、比较有意思的是置换的快速幂和开方、

学习资料:click here

 

Burnside引理与Polya定理

关于burnside引理其本质就是找出在每一种置换下不变元素的个数

burnside

然而使用搜索实现的burnside的统计复杂度过高、于是通过置换群推理得到polya定理、具体证明过程请看论文: 符文杰《Pólya原理及其应用》

下面给出polya定理的公式

polya

注意:对于圆环的模型来说、求循环节可以转化为求gcd、对于求gcd又可以用欧拉函数来优化、

环有len个节点,每个节点必须染色
color == 颜色数(无限)
len == 节点数
ans += color^(gcd(len, i)) (1<=i<=len)
if(len & 1) ans += len * color^(len / 2);
else ans += len / 2 * (color^(len / 2) + color^(len / 2 + 1))

代码如下:

LL polya() {  
    LL sum = 0;  
    for (int i = 1; i <= s; i++)  
        if (s % i == 0)  
            sum += Pow(c, i) * eular(s / i);  
    if (s & 1)  
        sum += s * Pow(c, (s >> 1) + 1);  
    else  
        sum += s / 2 * (Pow(c, (s >> 1) + 1) + Pow(c, s >> 1));  
    return sum / 2 / s;  
}  
View Code

 

学习资料:click here

 

鸽巢原理和容斥原理

这两个定理看概念好简单、却杀人于无形- -、智商常年被秀、、、、

 

容斥定理一般使用二进制压缩来实现:给定一个数组a,求1到n的整数中至少能整除a中一个元素的数有几个

typedef long long ll;
int a[MAX_M];
int n, m;
void solve() {
    ll res = 0;
    for (int i = 1; i < (1 << m); i++) {
        int num = 0;
        for (int j = i; j != 0; j >>= 1) num += j & i; // i的二进制表示中1的数量
        ll lcm = 1;
        for (int j = 0; j < m; j++) {
            if (i >> j & 1) {
                lcm = lcm / gcd(lcm, a[j]) * a[j];
                // 如果lcm大于n,则n/lcm=0 因此在溢出之前break
                if (lcm > n) break;
            }
        }
        if (num % 2 == 0) res -= n / lcm;
        else res += n / lcm;
    }
    printf("%d\n", res);
}
View Code

 

鸽巢定理更多用于证明、比如求最近点对中枚举周围最多6个点便是使用鸽巢定理证明、

 

学习资料:click here

 

 

然而容斥定理需要枚举所有集合之间的组合情况、复杂度过高、可用莫比乌斯函数反演来优化、

f(n)=g(d) (d|n) <==> g(n)=u(n/d)*f(d) (d|n)
f(d)是周期为d的约数的字符串的个数、g(d)是周期恰好为d的字符串的个数
由于f(d)已经可以高效的求出了、因此可以由右边的式子推出g(n)

若n可以被除1以外的完全平方数整除 u(n)=0
否则设n的质因数的个数为k u(n)=(-1)^k

由于整数分解可以在O(sqrt(n))时间内完成、所以u(n)也是
如果使用埃氏筛法、可以在O(n)时间内求出1-n所有的u值

求所有的由a-z组成的(不一定要使用所有字母)长度为n的字符串中,没有周期性的字符串的个数。

// 把n的约数的莫比乌斯函数值用map的形式返回  O(sqrt(n))
map<int, int> moebius(int n) {
    map<int, int> res;
    vector<int> primes;
    // 枚举n的质因数
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            primes.push_back(i);
            while (n % i == 0) n /= i;
        }
    }
    if(n != 1)primes.push_back(n);
    int m = primes.size();
    for (int i = 0; i < (1 << m); i++) { // 虽然要执行2^m次、但是这不超过n的约数个数
        int mu = 1, d = 1;
        for (int j = 0; j < m; j++) {
            if (i >> j & 1) {
                mu *= -1;
                d *= primes[j];
            }
        }
        res[d] = mu;
    }
    return res;
}
const int MOD = 10009;
//输入
int n;
void solve() {
    int res = 0;
    map<int, int> mu = moebius(n);
    for (map<int, int>::iterator it = mu.begin(); it != mu.end(); ++it) {
        res += it->second * mod_pow(26, n / it->first, MOD);
        res = (res % MOD + MOD) % MOD;
    }
    printf("%d\n", res);
}
View Code

 

快速傅里叶变换(FFT)

FFT主要用于多项式乘法、可学习郭晓旭论文《Fourier transform》

高精度乘法模板

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;

const double PI = acos(-1.0);
//复数结构体
struct complex
{
    double r,i;
    complex(double _r = 0.0,double _i = 0.0)
    {
        r = _r; i = _i;
    }
    complex operator +(const complex &b)
    {
        return complex(r+b.r,i+b.i);
    }
    complex operator -(const complex &b)
    {
        return complex(r-b.r,i-b.i);
    }
    complex operator *(const complex &b)
    {
        return complex(r*b.r-i*b.i,r*b.i+i*b.r);
    }
};
/*
 * 进行FFT和IFFT前的反转变换。
 * 位置i和 (i二进制反转后位置)互换
 * len必须去2的幂
 */
void change(complex y[],int len)
{
    int i,j,k;
    for(i = 1, j = len/2;i < len-1; i++)
    {
        if(i < j)swap(y[i],y[j]);
        //交换互为小标反转的元素,i<j保证交换一次
        //i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
        k = len/2;
        while( j >= k)
        {
            j -= k;
            k /= 2;
        }
        if(j < k) j += k;
    }
}
/*
 * 做FFT
 * len必须为2^k形式,
 * on==1时是DFT,on==-1时是IDFT
 */
void fft(complex y[],int len,int on)
{
    change(y,len);
    for(int h = 2; h <= len; h <<= 1)
    {
        complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
        for(int j = 0;j < len;j+=h)
        {
            complex w(1,0);
            for(int k = j;k < j+h/2;k++)
            {
                complex u = y[k];
                complex t = w*y[k+h/2];
                y[k] = u+t;
                y[k+h/2] = u-t;
                w = w*wn;
            }
        }
    }
    if(on == -1)
        for(int i = 0;i < len;i++)
            y[i].r /= len;
}
const int MAXN = 200010;
complex x1[MAXN],x2[MAXN];
char str1[MAXN/2],str2[MAXN/2];
int sum[MAXN];
int main()
{
    while(scanf("%s%s",str1,str2)==2)
    {
        int len1 = strlen(str1);
        int len2 = strlen(str2);
        int len = 1;
        while(len < len1*2 || len < len2*2)len<<=1;
        for(int i = 0;i < len1;i++)
            x1[i] = complex(str1[len1-1-i]-'0',0);
        for(int i = len1;i < len;i++)
            x1[i] = complex(0,0);
        for(int i = 0;i < len2;i++)
            x2[i] = complex(str2[len2-1-i]-'0',0);
        for(int i = len2;i < len;i++)
            x2[i] = complex(0,0);
        //求DFT
        fft(x1,len,1);
        fft(x2,len,1);
        for(int i = 0;i < len;i++)
            x1[i] = x1[i]*x2[i];
        fft(x1,len,-1);
        for(int i = 0;i < len;i++)
            sum[i] = (int)(x1[i].r+0.5);
        for(int i = 0;i < len;i++)
        {
            sum[i+1]+=sum[i]/10;
            sum[i]%=10;
        }
        len = len1+len2-1;
        while(sum[len] <= 0 && len > 0)len--;
        for(int i = len;i >= 0;i--)
            printf("%c",sum[i]+'0');
        printf("\n");
    }
    return 0;
}
View Code

利用FFT可以快速求取循环卷积

长度为n的数组和长度为m的数组卷积,结果是长度为n+m-1的数组。
使用FFT一定要注意控制好长度,长度要为2^k.而且大于等于len1+len2-1.这样可以保证不重叠。

题意:给出n条线段长度,问任取3根,组成三角形的概率 n<=10^5
const int MAXN = 400040;
complex x1[MAXN];
int a[MAXN/4];
long long num[MAXN];//100000*100000会超int
long long sum[MAXN];

int main()
{
    int T;
    int n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(num,0,sizeof(num));
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&a[i]);
            num[a[i]]++;
        }
        sort(a,a+n);
        int len1 = a[n-1]+1;
        int len = 1;
        while( len < 2*len1 )len <<= 1;
        for(int i = 0;i < len1;i++)
            x1[i] = complex(num[i],0);
        for(int i = len1;i < len;i++)
            x1[i] = complex(0,0);
        fft(x1,len,1);
        for(int i = 0;i < len;i++)
            x1[i] = x1[i]*x1[i];
        fft(x1,len,-1);
        for(int i = 0;i < len;i++)
            num[i] = (long long)(x1[i].r+0.5);
        len = 2*a[n-1];
        //减掉取两个相同的组合
        for(int i = 0;i < n;i++)
            num[a[i]+a[i]]--;
        //选择的无序,除以2
        for(int i = 1;i <= len;i++)
        {
            num[i]/=2;
        }
        sum[0] = 0;
        for(int i = 1;i <= len;i++)
            sum[i] = sum[i-1]+num[i];
        long long cnt = 0;
        for(int i = 0;i < n;i++)
        {
            cnt += sum[len]-sum[a[i]];
            //减掉一个取大,一个取小的
            cnt -= (long long)(n-1-i)*i;
            //减掉一个取本身,另外一个取其它
            cnt -= (n-1);
            //减掉大于它的取两个的组合
            cnt -= (long long)(n-1-i)*(n-i-2)/2;
        }
        //总数
        long long tot = (long long)n*(n-1)*(n-2)/6;
        printf("%.7lf\n",(double)cnt/tot);
    }
    return 0;
}
View Code

 

 

 

其他

 

高斯消元

#define eps 1e-9
const int MAXN = 220;
double a[MAXN][MAXN], x[MAXN];//方程的左边的矩阵和等式右边的值,求解之后x存的就是结果
int equ, var;//方程数和未知数个数
/*
*返回0表示无解,1表示有解
*/
int Gauss() {
    int i, j, k, col, max_r;
    for (k=0, col=0; k < equ && col < var; k++, col++) {
        max_r = k;
        for (i = k + 1; i < equ; i++)
            if (fabs(a[i][col]) > fabs(a[max_r][col]))
                max_r=i;
        if (fabs(a[max_r][col]) < eps) return 0;
        if (k != max_r) {
            for (j = col; j < var; j++)
                swap(a[k][j], a[max_r][j]);
                swap(x[k], x[max_r]);
        }
        x[k] /= a[k][col];
        for (j = col+1; j < var; j++) a[k][j] /= a[k][col];
        a[k][col] = 1;
        for (i = 0; i < equ; i++)
            if(i != k) {
                x[i] -= x[k] * a[i][k];
                for (j = col + 1; j < var; j++) a[i][j] -= a[k][j]*a[i][col];
                     a[i][col] = 0;
            }
    }
    return 1;
}
View Code

 


错排

f[1]=0 f[2]=1 f[n]=(n-1)*(f[n-1]+f[n-2])
f(n) = n![1-1/1!+1/2!-1/3!+……+(-1)^n*1/n!]

 

卡特兰数

卡特兰数是组合数学中一个常出现在各种计数问题中出现的数列
另类递归式: h(n)=((4*n-2)/(n+1))*h(n-1)
前几项1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440
所有的奇卡塔兰数Cn都满足n = 2^k-1、所有其他的卡塔兰数都是偶数
应用:
Cn表示长度2n的dyck word的个数。Dyck word是一个有n个X和n个Y组成的字串,且所有的部分字串皆满足X的个数大于等于Y的个数。XXXYYY XYXXYY XYXYXY XXYYXY XXYXYY
将上例的X换成左括号,Y换成右括号,Cn表示所有包含n组括号的合法运算式的个数:((())) ()(()) ()()() (())() (()())
Cn表示有n+1个叶子的二叉树的个数。
Cn表示所有不同构的含n个分枝结点的满二叉树的个数。
Cn表示所有在n*n格点中不越过对角线的单调路径的个数。
Cn表示通过连结顶点而将n + 2边的凸多边形分成三角形的方法个数。

 

几类放苹果问题

苹果m 盘子n 允许有空盘 放法数
不同 不同 允许 n^m
不同 不同 不允许 n!*S(m,n)
不同 相同 允许 S(m,1)+S(m,2)+...+S(m,n),m>=n
S(m,1)+S(m,2)+...+S(m,m),m<=n
不同 相同 不允许 S(m,n)
相同 不同 允许 C(m+n+1,m)
相同 不同 不允许 C(m-1,n-1)
相同 相同 允许 F(n,m)
相同 相同 不允许 F(n,m-n)

F(n,m)是简单放苹果问题。S(m,n)是第二类Stirling数
递推公式:

int F(int m,int n) {
    if (m == 0 || n == 1)
        return 1;
    if (m < n)
        return (F(m, m));
    else
        return (F(m - n, n) + F(m, n - 1));
}
int S(int m, int n) {
    if (m == n || n == 1) return 1;
    return m * S(n - 1, m) + S(m - 1, m - 1);
}
View Code

 

其实博弈论也应算在数学里面、但是我还是打算单独将博弈列出来、

 

最后、数学就总结到这里了、

           -----    黑白是一种态度

posted @ 2015-07-07 23:58  Mzx0821  阅读(214)  评论(0)    收藏  举报