多重排列——指数型母函数


一、指数型母函数

1.结构

对于序列 a 0 a_0 a0, a 1 a_1 a1, a 2 a_2 a2…… a k a_k ak,的函数
G(x) = a 0 a_0 a0+ a 1 1 ! \frac{a_1}{1!} 1!a1x+ a 2 2 ! \frac{a_2}{2!} 2!a2 x 2 x^2 x2+……+ a k k ! \frac{a_k}{k!} k!ak x k x^k xk
称为序列 a 0 a_0 a0, a 1 a_1 a1, a 2 a_2 a2…… a k a_k ak对应的指数型母函数。

对于一个多重集,其中 a 1 a_1 a1重复 n 1 n_1 n1次, a 2 a_2 a2重复 n 2 n_2 n2次…… a k a_k ak重复 n k n_k nk次,从中取r个排列的不同排列数所对应的指数型母函数为:
G(x) = (1+ x 1 ! \frac{x}{1!} 1!x+…+ x n 1 n 1 ! \frac{x^{n_1}}{{n_1}!} n1!xn1) (1+ x 1 ! \frac{x}{1!} 1!x+…+ x n 2 n 2 ! \frac{x^{n_2}}{{n_2}!} n2!xn2)… (1+ x 1 ! \frac{x}{1!} 1!x+…+ x n k n k ! \frac{x^{n_k}}{{n_k}!} nk!xnk)
将其展开后, x r x^r xr 的系数为 k r ! \frac{k}{r!} r!k,所以将系数乘以 r ! r! r!就可以得到排列数了。

2.思想

我们先讨论多重排列是如何计算的,其中 a 1 a_1 a1 n 1 n_1 n1个, a 2 a_2 a2 n 2 n_2 n2个…… a k a_k ak n k n_k nk个,且 n 1 n_1 n1+ n 2 n_2 n2+……+ n k n_k nk = N,求这N个物品的全排列。
我们是通过公式 N ! n 1 ! n 2 ! . . . n k ! \frac{N!}{{n_1}{!}{n_2}{!}{...}{n_k}{!}} n1!n2!...nk!N!来计算。
那么我们再回到上面的指数型母函数,对于从中取出r个是存在不同的组合的,而我们最后的结果其实是把所有组合的排列数加在一起。
而上式的展开式中对应的系数就是每种组合所对应的排列数除以r!的和,那么最后乘以r!就是我们的结果。

那么整体的代码实现还是可以参考之前普通母函数的写法,只需要做一些调整和修改就行了。


二、例题

1.排列组合

排列组合

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
double c1[maxn],c2[maxn]; // 注意为浮点型
int s[maxn];
double f[11];
void init() {
    f[0] = 1;
    for (int i = 1; i <= 10; i++) {
        f[i] = f[i - 1] * i;
    }
}
int main() {
    int n,m;
    init();
    while (cin>>n>>m){
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        memset(s,0,sizeof(s));
       for (int i = 0; i < n; i++){
            cin>>s[i];
       }
        for (int i = 0; i <= s[0]; i++) c1[i] = 1.0 / f[i];
        for (int i = 1; i < n; i++) {
            memset(c2,0,sizeof(c2));
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k <= s[i] && k + j <= m; k++) {
                    c2[j + k] += c1[j] * 1.0 / f[k]; //注意这里
                }
            }
            memcpy(c1, c2, sizeof c2);
        }
       printf("%.0f\n", c1[m]*f[m]);//对结果乘上m!,即为结果 
    }
    return 0;
}

2.红色病毒(联系泰勒展开)

红色病毒

与之前的区别在于物品A、C的取法仅可为偶数,可以得到母函数为:
G(x) = (1+ x 1 ! \frac{x}{1!} 1!x+…+ x n 1 n 1 ! \frac{x^{n_1}}{{n_1}!} n1!xn1) (1+ x 2 2 ! \frac{x^2}{2!} 2!x2+…+ x n 2 n 2 ! \frac{x^{n_2}}{{n_2}!} n2!xn2)(1+ x 1 ! \frac{x}{1!} 1!x+…+ x n 3 n 3 ! \frac{x^{n_3}}{{n_3}!} n3!xn3) (1+ x 2 2 ! \frac{x^2}{2!} 2!x2+…+ x n 4 n 1 ! \frac{x^{n_4}}{{n_1}!} n1!xn4)

同时此时N的范围为1<=N<2^64,我们最后需要输出其最后两位,如果是直接使用原来的方法将无法在规定时间内求解,毕竟是O( n 3 n^3 n3),所以我们引入了 e x e^x ex的泰勒展开:
e x e^x ex = 1+ x 1 ! \frac{x}{1!} 1!x+ x 2 2 ! \frac{x^2}{2!} 2!x2+ x 3 3 ! \frac{x^3}{3!} 3!x3……
e − x e^{-x} ex = 1- x 1 ! \frac{x}{1!} 1!x+ x 2 2 ! \frac{x^2}{2!} 2!x2- x 3 3 ! \frac{x^3}{3!} 3!x3……
那么我们就可以构造出:
G(x) = e 2 x e^{2x} e2x ( e x + e − x 2 ) 2 {{(}\frac{{e^x}+{e^{-x}}}{2}{)}^2} (2ex+ex)2 = 1 4 \frac{1}{4} 41 e 2 x e^{2x} e2x ( e 2 x + 2 + e − 2 x ) {(}e^{2x}+2+e^{-2x}{)} (e2x+2+e2x)= 1 4 \frac{1}{4} 41 ( e 4 x + 2 e 2 x + 1 ) {(}e^{4x}+2e^{2x}+1{)} (e4x+2e2x+1)
化简之后我们再对其展开,就可以直接找到原先母函数中第n项的系数:
第n项为 1 4 ( \frac{1}{4}{(} 41( ( 4 x ) n n ! \frac{{(}{4x}{)}^n}{n!} n!(4x)n+ 2 2 2 ( 2 x ) n n ! ) \frac{{(}{2x}{)}^n}{n!}{)} n!(2x)n)= ( 4 n − 1 + 2 n − 1 ) x n n ! {(}4^{n-1}+2^{n-1}{)}\frac{x^n}{n!} (4n1+2n1)n!xn
那么就可以直接快速幂求解然后取两位,同时我们只需要保证最后两位的准确,所以可以对其进行取模操作,模取100即可。

#include<bits/stdc++.h>
using namespace std;
const int mod = 100;
int qk(long long a,long long b){
    int result = 1;
    while(b){
        if (b&1){
            result =(result*a)%mod;
        }
        b>>=1;
        a=(a*a) % mod;
    }
    return result;
}
int main(){
    long long n;
    while(cin>>n&&n){
        long long m;
        long long cnt = 1;
        while(n--){
            cin>>m;
            cout<<"Case "<<cnt++<<": ";
            cout<<(qk(4,m-1)+qk(2,m-1))%mod<<endl;
        }
        cout<<endl;
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值