一、指数型母函数
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}
e−x = 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+e−x)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+e−2x)=
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!}
(4n−1+2n−1)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;
}