[指数型生成函数专练]chocolate,红色病毒问题,排列组合,字串数

T1:chocolate

题目

已帮大家翻译了,不要去UVA或者luogu上面交,卡精度,
在这里插入图片描述
在2100年,ACM巧克力将成为世界上最受欢迎的食品之一。

“绿色,橙色,棕色,红色…”,彩色糖衣壳也许是ACM巧克力最吸引人的特征。您见过多少种颜色?如今,据说ACM从二十四种颜色的调色板中进行选择来绘制其美味的糖果。

一天,桑迪在一大包ACM巧克力上玩游戏,其中包含五种颜色(绿色,橙色,棕色,红色和黄色)。每次他从包装中取出一个巧克力并将其放在桌子上。如果桌子上有两个颜色相同的巧克力,他会把它们都吃掉。他发现一件很有趣的事情,在大多数情况下,桌上总是有2或3块巧克力。

现在,问题来了,如果包装中有C种颜色的ACM巧克力(颜色均匀分布),那么从包装中取出N颗巧克力后,桌上有M颗巧克力的概率是多少?您可以编写一个程序来解决吗?

输入
此问题的输入文件包含几个测试用例,每行一个
对于每种情况,都有三个非负整数:C(C <= 100),N和M(N,M <= 1000000)
输入由包含单个零的行终止。
输出
输出应该是每行一个实数,显示每种情况的概率,四舍五入到小数点后三位

样本输入
5 100 2

0
样本输出
0.625

题解

首先我们由题意知道如果桌子上有两个颜色相同的巧克力,会被吃掉,所以最后桌子上的巧克力一定是互不相同的,也就是有 m m 种不同的糖果留在上面,所以我们可以特判当 m > c m>c 时,是无解的


m > n m>n 时,脑子也想得出来是无解的,一共就 n n 颗,你从哪里变出 m n m-n


再来每次都是两颗两颗地吃,所以 n m n-m 也就是吃的颗数一定是偶数,所以也可以特判一下


接下来进入母函数板块
首先是
前面提到过最后剩在桌上的糖果一定都是互不相同的,剩 m m 颗,也就是剩 m m 种,那么这 m m 种糖果拿的数量一定是奇数(最后剩下一个不能吃),剩下的 c m c-m 种糖果拿的一定是偶数(都被两两吃掉),如果你不能理解就算了 ,且吃的顺序不同算不同的方法加上是等概率,那么总情况就是 c n c^n
我们考虑最普通的指数型函数
1 + x 1 ! + x 2 2 ! + . . . + x n n ! + . . . = e x 1+\frac{x}{1!}+\frac{x^2}{2!}+...+\frac{x^n}{n!}+...=e^x
奇数的糖果的话就要想办法消掉 x x^{偶数项} ,偶数的糖果就要想办法消掉 x x^{奇数项}
奇数项: e x e x 2 \frac{e^x-e^{-x}}{2}
偶数项: e x + e x 2 \frac{e^x+e^{-x}}{2}
在这里插入图片描述
不要慌,我们来带一下 e x = 1 + x 1 ! + ( x ) 2 2 ! + . . . + ( x ) n n ! e^{-x}=1+\frac{-x}{1!}+\frac{(-x)^2}{2!}+...+\frac{(-x)^n}{n!}
以奇数项为例,偶数项相信大家看完后会秒懂
e x e x 2 = ( 1 1 ) + ( x ( x ) ) + ( x 2 ( x ) 2 ) + . . . + ( x n ( x ) n ) + . . . 2 \frac{e^x-e^{-x}}{2}=\frac{(1-1)+(x-(-x))+(x^2-(-x)^2)+...+(x^n-(-x)^n)+...}{2} = 2 x + 2 x 3 + 2 x 5 + . . . 2 = x + x 3 + x 5 + . . . =\frac{2x+2x^3+2x^5+...}{2}=x+x^3+x^5+...
在这里插入图片描述
那么把每一种糖果对应的母函数乘起来,即
( e x e x 2 ) m + ( e x e x 2 ) c m (\frac{e^x-e^{-x}}{2})^m+(\frac{e^x-e^{-x}}{2})^{c-m}
运用二项式展开定理得到,看来我还得去学学这玩意儿
i = 0 m   ( 1 ) i C m i e ( m i ) x e i x 2 m × i = 0 c m C c m i e ( c m i ) x e i x 2 c m \frac{\sum_{i=0}^m\ (-1)^i*C_m^i*e^{(m-i)*x}*e^{-ix}}{2^m}×\frac{\sum_{i=0}^{c-m}C_{c-m}^i*e^{(c-m-i)x}*e^{-ix}}{2^{c-m}}
i = 0 m ( 1 ) i C m i e ( m 2 i ) x × i = 0 c m C c m i e ( c m 2 i ) x 2 c \frac{\sum_{i=0}^m(-1)^i*C_m^i*e^{(m-2i)x}×\sum_{i=0}^{c-m}C_{c-m}^i*e^{(c-m-2i)x}}{2^c}
我们先把分母放一放,先只看分子,对其进行化简
i = 0 m j = 0 c m ( 1 ) i C m i C c m j e ( c 2 i 2 j ) x \sum_{i=0}^m\sum_{j=0}^{c-m}(-1)^i*C_m^i*C_{c-m}^j*e^{(c-2i-2j)x}
将这个的封闭式展开,变成
k = 0 i 0 m , j 0 c m ( 1 ) i C m i C c m j ( c 2 i 2 j ) k k ! x k \sum_{k=0}^∞\frac{\sum_{i\le0\le m,j\le0\le c-m}(-1)^i*C_m^i*C_{c-m}^j*{(c-2i-2j)^k}}{k!}*x^k
但是这里我们不能忘记上面的分母和总情况 2 c , c n 2^c,c^n ,最后剩下的是哪 m m 种,还有一个 C c m C_c^m
所以最后第 n n 项的系数就是 C c m 2 c c n i 0 m , j 0 c m ( 1 ) i C m i C c m j ( c 2 i 2 j ) k \frac{C_c^m}{2^c*c^n}*\sum_{i\le0\le m,j\le0\le c-m}(-1)^i*C_m^i*C_{c-m}^j*{(c-2i-2j)^k}

code

本来刚开始快速幂和组合数都不想开 d o u b l e double ,但是同胞告诉我会炸 i n t int
在这里插入图片描述

#include <cstdio>
#include<iostream>
using namespace std;
#define MAXN 200
#define _for(i,a,b) for(int i=(a);i<=(b);++i)
double C[MAXN + 5][MAXN + 5];

double qkpow ( double x, int y ) {
	double ans = 1;
	while ( y ) {
		if ( y & 1 )
			ans = ans * x;
		x *= x;
		y >>= 1;
	}
	return ans;
}

void init () {
	C[0][0] = 1;
	for ( int i = 1;i <= MAXN;i ++ ) {
		C[i][0] = 1;
		for ( int j = 1;j <= i;j ++ )
			C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
	}
}

int main() {
	int n, c, m;
	init();
	while ( scanf ( "%d", &c ) && c != 0 ) {
		scanf ( "%d %d", &n, &m );
		if ( m > c || m > n || ( n - m ) & 1 ) {
			printf ( "0.000\n" );
			continue;
		}
		double result = 0;
		for ( int i = 0;i <= m;i ++ )
			for ( int j = 0;j <= c - m;j ++ )
				result += ( i & 1 ? -1 : 1 ) * C[m][i] * C[c - m][j] * qkpow ( c - ( i << 1 ) - ( j << 1 ), n );
		result *= C[c][m] / qkpow ( 2, c ) / qkpow ( c, n );
		printf ( "%.3f\n", result );
	}
	return 0;
}

T2:“红色病毒”问题

题目

医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶均是成对出现的。
现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.

Input
每组输入的第一行是一个整数T,表示测试实例的个数,下面是T行数据,每行一个整数N(1<=N<2^64),当T=0时结束.
Output
对于每个测试实例,输出字符串个数的最后两位,每组输出后跟一个空行.

Sample Input
4
1
4
20
11
3
14
24
6
0
Sample Output
Case 1: 2
Case 2: 72
Case 3: 32
Case 4: 0

Case 1: 56
Case 2: 72
Case 3: 56

题解

对于 B , D B,D 是没有限制的,那么它们的生成函数则是普通型
1 + x 1 ! + x 2 2 ! + x 3 3 ! . . . . . . = e x 1+\frac{x}{1!}+\frac{x^2}{2!}+\frac{x^3}{3!}......=e^x
A , C A,C 的要求是必须出现偶数次,当然不出现也行,所以他们的生成函数就只能保留 x x^{偶数次}
1 + x 2 2 ! + x 4 4 ! + . . . . . . = e x + e x 2 1+\frac{x^2}{2!}+\frac{x^4}{4!}+......=\frac{e^x+e^{-x}}{2}
所以先写T1的题解不是没有逻辑的
将四个的生成函数闭形式相乘来化简试试
( e x ) 2 ( e x + e x 2 ) 2 = e 2 x e 2 x + e 2 x + 2 4 = e 4 x + 2 e 2 x + 1 4 (e^x)^2*(\frac{e^x+e^{-x}}{2})^2=e^{2x}*\frac{e^{2x}+e^{-2x}+2}{4}=\frac{e^{4x}+2e^{2x}+1}{4}
然后还原成母函数
e 4 x = 1 + 4 x 1 ! + ( 4 x ) 2 2 ! + . . . ( 4 x ) n n ! + . . . . e^{4x}=1+\frac{4x}{1!}+\frac{(4x)^2}{2!}+...\frac{(4x)^n}{n!}+....
2 e 2 x = 2 ( 1 + 2 x 1 ! + ( 2 x ) 2 2 ! + . . . + ( 2 x ) n n ! + . . . ) = 2 + 4 x 1 ! + 8 x 2 ! + . . . + 2 ( 2 x ) n n ! + . . . 2e^{2x}=2(1+\frac{2x}{1!}+\frac{(2x)^2}{2!}+...+\frac{(2x)^n}{n!}+...)=2+\frac{4x}{1!}+\frac{8x}{2!}+...+\frac{2(2x)^n}{n!}+...
所以
e 4 x + 2 e 2 x + 1 4 = 1 4 ( 4 + 4 x + 2 2 x 1 ! + ( 4 x ) 2 + 2 ( 2 x ) 2 2 ! + . . . + ( 4 x ) n + 2 ( 2 n ) n n ! ) + . . . \frac{e^{4x}+2e^{2x}+1}{4}=\frac{1}{4}*(4+\frac{4x+2*2x}{1!}+\frac{(4x)^2+2*(2x)^2}{2!}+...+\frac{(4x)^n+2(2n)^n}{n!})+...
= 1 4 ( 4 + ( 4 + 2 2 ) x 1 ! + ( 4 2 + 2 2 2 ) x 2 2 ! + . . . ( 4 n + 2 2 n ) x n n ! + . . . ) =\frac{1}{4}*(4+(4+2*2)\frac{x}{1!}+(4^2+2*2^2)\frac{x^2}{2!}+...(4^n+2*2^n)\frac{x^n}{n!}+...)
常数项是第零项,把 1 4 \frac{1}{4} 乘进去,所以第 n n 项的系数应该是
1 4 ( 4 n + 2 2 n ) = 4 n 1 + 2 n 1 \frac{1}{4}*(4^{n}+2*2^{n})=4^{n-1}+2^{n-1}

code

#include <cstdio>
#include <iostream>
using namespace std;
#define mod 100
#define LL unsigned long long
LL n;

LL qkpow ( LL x, LL y ) {
	LL ans = 1;
	while ( y ) {
		if ( y & 1 )
			ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}

int main() {
	int T; 
	while ( scanf ( "%d", &T ) && T != 0 ) {
		int Case = 0;
		while ( T -- ) {
			cin >> n;
			printf ( "Case %d: %lld\n", ++ Case, ( qkpow ( 2, n - 1 ) + qkpow ( 4, n - 1 ) ) % mod );	
		}
		printf ( "\n" ); 
	}
	return 0;
}

T3:排列组合

题目

有n种物品,并且知道每种物品的数量。要求从中选出m件物品的排列数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有"AB","BA"两种。
Input
每组输入数据有两行,第一行是二个数n,m(1<=m,n<=10),表示物品数,第二行有n个数,分别表示这n件物品的数量。
Output
对应每组数据输出排列数。(任何运算不会超出2^31的范围)
Sample Input
2 2
1 1
Sample Output
2

题解

我们对于每一种物品数,都可以写出其生成函数
i [ 1 , n ] 1 + x 1 ! + x 2 2 ! + x 3 3 ! + . . . + x n u m [ i ] n u m [ i ] ! i∈[1,n],1+\frac{x}{1!}+\frac{x^2}{2!}+\frac{x^3}{3!}+...+\frac{x^{num[i]}}{num[i]!}
d p [ j ] dp[j] 表示选了 j j 个物品的排列数,那么对于第 i i 种物品而言,我们枚举该种物品选的个数是 k [ 1 , n u m [ i ] ] k∈[1,num[i]] ,那么排列数就要加上 k k 个物品的排列数,但是因为它们是一种,顺序没有用,所以要除掉 k ! k!
于是可以写出转移方程式 d p [ j + k ] = d p [ j + k ] + d p [ j ] / k ! dp[j+k]=dp[j+k]+dp'[j]/k!
在这里插入图片描述

code

这就像是一种模板了。。

#include <cstdio>
#include <cstring>
#define MAXN 15
int n, m;
int fac[MAXN], num[MAXN];
double dp1[MAXN], dp2[MAXN];

void init () {
	fac[0] = fac[1] = 1;
	for ( int i = 2;i < MAXN;i ++ )
		fac[i] = i * fac[i - 1]; 
}

int main() {
	init();
	while ( ~ scanf ( "%d %d", &n, &m ) ) {
		for ( int i = 1;i <= n;i ++ )
			scanf ( "%d", &num[i] );
		memset ( dp1, 0, sizeof ( dp1 ) );
		memset ( dp2, 0, sizeof ( dp2 ) );
		for ( int i = 0;i <= num[1];i ++ )
			dp2[i] = 1.0 / fac[i];
		for ( int i = 2;i <= n;i ++ ) {
			for ( int j = 0;j <= m;j ++ )
				for ( int k = 0;k <= num[i];k ++ )
					if ( j + k > m )
						break;
					else
						dp1[j + k] += dp2[j] / fac[k];
			for ( int j = 0;j <= m;j ++ ) {
				dp2[j] = dp1[j];
				dp1[j] = 0;
			}
		}
		printf ( "%.0lf\n", dp2[m] * fac[m] );
	}
	return 0;
}

T4:字串数

一个A和两个B一共可以组成三种字符串:“ABB”,“BAB”,“BBA”.
给定若干字母和它们相应的个数,计算一共可以组成多少个不同的字符串.
Input
每组测试数据分两行,第一行为n(1<=n<=26),表示不同字母的个数,第二行为n个数A1,A2,…,An(1<=Ai<=12),表示每种字母的个数.测试数据以n=0为结束.
Output
对于每一组测试数据,输出一个m,表示一共有多少种字符串.
Sample Input
2
1 2
3
2 2 2
0
Sample Output
3
90

题解

这道题跟上面第三题有点相似,我们可以先看对于 n n 个字符,一共可以拼凑出 n ! n! 个字符串
但是我们知道如果有两个 A A 那么 A 0 A 1 A_0A_1 A 1 A 0 A_1A_0 我们是看不出来的,那么就应该排除掉它们的影响
所以这道题的公式就是 n ! A 0 ! A 1 ! . . . A n ! \frac{n!}{A_0!A_1!...A_n!}
其实这道题的本质是卡我们的高精,哎~具体的代码理解在 c o d e code 里面有详解,你一定会看得懂的

code

#include <cstdio>
#include <cstring>
#define MAXN 30
#define MAX 1000
int n, len;
int A[MAXN];
int res[MAX];

void Res_bit () { 
	for ( int i = 0;i < len;i ++ )
		if ( res[i] > 9 ) {
			if ( i == len - 1 )
				len ++;
			res[i + 1] += res[i] / 10;
			res[i] %= 10;
		}
}

void Fac ( int x ) {
	res[0] = x;
	len = 1;
	Res_bit();
	while ( x > 2 ) {
//1是不必要乘的,0是不能乘的
//这个条件的下限就是x=3,走进去就变成了2,当x=2时就不必要了 
		x --;
//大数乘法其实跟竖式乘法是一样的
//我们算A*B是用B的个位依次去乘A的每一位加上B的十位去乘A的每一位的结果...以此类推
		for ( int i = 0;i < len;i ++ )
			res[i] *= x;
		Res_bit ();//每一次都要进行大数进位 
	}
}

void div ( int x ) {
//除法也可以用竖式来理解,从最高位开始除
//除不动就往下一位(商0),除得动就把余数传给下一位 
	for ( int i = len - 1;i >= 0;i -- )
		if ( res[i] < x ) {
//有的人可能会疑问感觉每次除法都只少了一位
//其实不然,在i循环减小时,如果是从最高位开始缩位
//len其实是同步减小了的,上面的进位同理 
			if ( i == len - 1 )
				len --;
			res[i - 1] += res[i] * 10;
			res[i] = 0;
		}
		else {
			if ( i > 0 )
				res[i - 1] += ( res[i] % x ) * 10;
			res[i] = res[i] / x;
		}
}

int main() {
	while ( scanf ( "%d", &n ) && n ) {
		int tot = 0;
		for ( int i = 1;i <= n;i ++ ) {
			scanf ( "%d", &A[i] );
			tot += A[i];
		}
		Fac ( tot );
		for ( int i = 1;i <= n;i ++ )
			for ( int j = 2;j <= A[i];j ++ )
				div ( j );
		for ( int i = len - 1;i >= 0;i -- )
			printf ( "%d", res[i] );
		printf ( "\n" );
		memset ( res, 0, sizeof ( res ) );
	}
	return 0;
} 

在这里插入图片描述
我好想写得顺序出了问题,我是fu应该先写生成函数的学习博客,然后再写题解
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Emm_Titan/article/details/103723023