暑期集训母函数、各种数

母函数

母函数,又称生成函数,是ACM竞赛中经常使用的一种解题算法,常用来解决组合方面的题目。

母函数通常解决类似如下的问题:

给5张1元,4张2元,3张5元,要得到15元,有多少种组合?

解题过程

解题时,首先要写出表达式,通常是多项的乘积,每项由多个x^y组成。如(1+x+x^2)(1+x^4+x^8)(x^5+x^10+x^15)。

通用表达式为

(x^(v[0]*n1[0])+x^(v[0]*(n1[0]+1))+x^(v[0]*(n1[0]+2))+...+x^(v[0]*n2[0]))
(x^(v[1]*n1[1])+x^(v[1]*(n1[1]+1))+x^(v[1]*(n1[1]+2))+...+x^(v[1]*n2[1]))
...
(x^(v[K]*n1[K])+x^(v[K]*(n1[K]+1))+x^(v[K]*(n1[K]+2))+...+x^(v[K]*n2[K]))

K对应具体问题中物品的种类数。

v[i]表示该乘积表达式第i个因子的权重,对应于具体问题的每个物品的价值或者权重。

n1[i]表示该乘积表达式第i个因子的起始系数,对应于具体问题中的每个物品的最少个数,即最少要取多少个。

n2[i]表示该乘积表达式第i个因子的终止系数,对应于具体问题中的每个物品的最多个数,即最多要取多少个。

对于表达式(1+x+x^2)(x^8+x^10)(x^5+x^10+x^15+x^20),v[3]={1,2,5},n1[3]={0,4,1},n2[3]={2,5,4}。

解题的关键是要确定v、n1、n2数组的值。

通常n1都为0,但有时候不是这样。

n2有时候是无限大。

之后就实现表达式相乘,从第一个因子开始乘,直到最后一个为止。此处通常使用一个循环,循环变量为i。每次迭代的计算结果放在数组a中。计算结束后,a[i]表示权重i的组合数,对应具体问题的组合数。

循环内部是把每个因子的每个项和a中的每个项相乘,加到一个临时的数组b的对应位(这里有两层循环,加上最外层循环,总共有三层循环),之后就把b赋给a。

这些过程通常直接套用模板即可。

 

例题:Square Coins

People in Silverland use square coins. Not only they have square shapes but also their values are square numbers. Coins with values of all square numbers up to 289 (=17^2), i.e., 1-credit coins, 4-credit coins, 9-credit coins, ..., and 289-credit coins, are available in Silverland. 
There are four combinations of coins to pay ten credits: 

ten 1-credit coins, 
one 4-credit coin and six 1-credit coins, 
two 4-credit coins and two 1-credit coins, and 
one 9-credit coin and one 1-credit coin. 

Your mission is to count the number of ways to pay a given amount using coins of Silverland. 

Input

The input consists of lines each containing an integer meaning an amount to be paid, followed by a line containing a zero. You may assume that all the amounts are positive and less than 300. 

Output

For each of the given amount, one line containing a single integer representing the number of combinations of coins should be output. No other characters should appear in the output. 

Sample Input

2
10
30
0

Sample Output

1
4
27
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>

using namespace std;

int a[310],b[310];

int main()
{
    int n;
    while(~scanf("%d",&n)){
        if(!n){
            break;
        }
        for(int i=0;i<=300;i++){
            a[i]=1;
            b[i]=0;
        }
        for(int i = 2;i <= n;i++){
            for(int j = 0;j <= n;j++){
                for(int k = 0;k+j<=n;k+=i*i){
                    b[k+j]+=a[j];
                }
            }
            for(int j = 0;j <= n;j++){
                a[j]=b[j];
                b[j]=0;
            }
        }
        printf("%d\n",a[n]);
    }
    return 0;
}

卡特兰数

卡特兰数是一种经典的组合数,经常出现在各种计算中,其前几项为 : 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...

卡特兰数一般的计算公式:这里写图片描述 
另类递推公式:C(n)=C(n-1)*((4*n-2)/(n+1))

可用于解决的问题:传送门

例题:Train Problem II

 

As we all know the Train Problem I, the boss of the Ignatius Train Station want to know if all the trains come in strict-increasing order, how many orders that all the trains can get out of the railway. 

Input

The input contains several test cases. Each test cases consists of a number N(1<=N<=100). The input is terminated by the end of file. 

Output

For each test case, you should output how many ways that all the trains can get out of the railway. 

Sample Input

1
2
3
10

Sample Output

1
2
5
16796

Hint

The result will be very large, so you may not process it by 32-bit integers.
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int a[110][110];
int b[110];

void Catalan()
{
    int i,j,len,carry,tmp;
    a[1][0]=b[1]=1;
    len = 1;
    for(int i = 2; i <= 100; i++){
        for(j = 0; j < len; j++){
            a[i][j] = a[i-1][j]*(4*i-2);
        }
        carry = 0;
        for(j = 0; j < len; j++){
            tmp = carry + a[i][j];
            a[i][j] = tmp%10;
            carry = tmp/10;
        }
        while(carry){
            a[i][len++] = carry % 10;
            carry /= 10;
        }
        for(j = len-1; j >= 0; j--){
            tmp = carry*10+a[i][j];
            a[i][j] = tmp/(i+1);
            carry = tmp%(i+1);
        }
        while(!a[i][len-1]){
            len--;
        }
        b[i] = len;
    }
}

int main()
{
    int n;
    Catalan();
    while(~scanf("%d",&n)){
        for(int i = b[n]-1; i >= 0; i--){
            printf("%d",a[n][i]);
        }
        printf("\n");
    }
    return 0;
}

斯特林公式

斯特灵公式是一条用来取n阶乘近似值的数学公式。一般来说,当n很大的时候,n阶乘的计算量十分大,所以斯特灵公式十分好用。从图中可以看出,即使在n很小的时候,斯特灵公式的取值已经十分准确。

公式为:   n! \approx \sqrt{2\pi n}\, \left(\frac{n}{e}\right)^{n}.

斯特林公式可以用来估算某数的大小,结合lg可以估算某数的位数,或者可以估算某数的阶乘是另一个数的倍数。

利用斯特灵公式求解n!的位数:

易知整数n的位数为[lgn]+1。.利用Stirling公式计算n!结果的位数时,可以两边取对数,得:
    log10(n!) = log10(2*n*Pi)/2+n*log10(n/e)
则答案为:
    ans = log10(2*n*Pi)/2+n*log10(n/e) + 1

 

例题:N的阶乘的长度 V2(斯特林近似)

输入N求N的阶乘的10进制表示的长度。例如6! = 720,长度为3。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 1000) 
第2 - T + 1行:每行1个数N。(1 <= N <= 10^9)

Output

共T行,输出对应的阶乘的长度。

Sample Input

3
4
5
6

Sample Output

2
3
3
#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

const double Pi=acos(-1);
const double e = 2.718281828459045;

int main()
{
    long long T,n;
    scanf("%lld",&T);
    while(T--){
        scanf("%lld",&n);
        printf("%lld\n",(long long)(log10(2.0*Pi*n)/2.0+n*log10(n/e)+1));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值