文章目录
- T1:chocolate
- 题目
- 题解
- code
- T2:“红色病毒”问题
- 题目
- 题解
- code
- T3:排列组合
- 题目
- 题解
- code
- T4:字串数
- 题解
- code
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
题解
首先我们由题意知道如果桌子上有两个颜色相同的巧克力,会被吃掉,所以最后桌子上的巧克力一定是互不相同的,也就是有mmm种不同的糖果留在上面,所以我们可以特判当m>cm>cm>c时,是无解的
当m>nm>nm>n时,脑子也想得出来是无解的,一共就nnn颗,你从哪里变出m−nm-nm−n颗
再来每次都是两颗两颗地吃,所以n−mn-mn−m也就是吃的颗数一定是偶数,所以也可以特判一下
接下来进入母函数板块
首先是
前面提到过最后剩在桌上的糖果一定都是互不相同的,剩mmm颗,也就是剩mmm种,那么这mmm种糖果拿的数量一定是奇数(最后剩下一个不能吃),剩下的c−mc-mc−m种糖果拿的一定是偶数(都被两两吃掉),如果你不能理解就算了 ,且吃的顺序不同算不同的方法加上是等概率,那么总情况就是cnc^ncn
我们考虑最普通的指数型函数
1+x1!+x22!+...+xnn!+...=ex1+\frac{x}{1!}+\frac{x^2}{2!}+...+\frac{x^n}{n!}+...=e^x1+1!x+2!x2+...+n!xn+...=ex
奇数的糖果的话就要想办法消掉x偶数项x^{偶数项}x偶数项,偶数的糖果就要想办法消掉x奇数项x^{奇数项}x奇数项
奇数项:ex−e−x2\frac{e^x-e^{-x}}{2}2ex−e−x
偶数项:ex+e−x2\frac{e^x+e^{-x}}{2}2ex+e−x
不要慌,我们来带一下e−x=1+−x1!+(−x)22!+...+(−x)nn!e^{-x}=1+\frac{-x}{1!}+\frac{(-x)^2}{2!}+...+\frac{(-x)^n}{n!}e−x=1+1!−x+2!(−x)2+...+n!(−x)n
以奇数项为例,偶数项相信大家看完后会秒懂
ex−e−x2=(1−1)+(x−(−x))+(x2−(−x)2)+...+(xn−(−x)n)+...2\frac{e^x-e^{-x}}{2}=\frac{(1-1)+(x-(-x))+(x^2-(-x)^2)+...+(x^n-(-x)^n)+...}{2}2ex−e−x=2(1−1)+(x−(−x))+(x2−(−x)2)+...+(xn−(−x)n)+...=2x+2x3+2x5+...2=x+x3+x5+...=\frac{2x+2x^3+2x^5+...}{2}=x+x^3+x^5+...=22x+2x3+2x5+...=x+x3+x5+...
那么把每一种糖果对应的母函数乘起来,即
(ex−e−x2)m+(ex−e−x2)c−m(\frac{e^x-e^{-x}}{2})^m+(\frac{e^x-e^{-x}}{2})^{c-m}(2ex−e−x)m+(2ex−e−x)c−m
运用二项式展开定理得到,看来我还得去学学这玩意儿
∑i=0m(−1)i∗Cmi∗e(m−i)∗x∗e−ix2m×∑i=0c−mCc−mi∗e(c−m−i)x∗e−ix2c−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}}2m∑i=0m (−1)i∗Cmi∗e(m−i)∗x∗e−ix×2c−m∑i=0c−mCc−mi∗e(c−m−i)x∗e−ix
∑i=0m(−1)i∗Cmi∗e(m−2i)x×∑i=0c−mCc−mi∗e(c−m−2i)x2c\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}2c∑i=0m(−1)i∗Cmi∗e(m−2i)x×∑i=0c−mCc−mi∗e(c−m−2i)x
我们先把分母放一放,先只看分子,对其进行化简
∑i=0m∑j=0c−m(−1)i∗Cmi∗Cc−mj∗e(c−2i−2j)x\sum_{i=0}^m\sum_{j=0}^{c-m}(-1)^i*C_m^i*C_{c-m}^j*e^{(c-2i-2j)x}i=0∑mj=0∑c−m(−1)i∗Cmi∗Cc−mj∗e(c−2i−2j)x
将这个的封闭式展开,变成
∑k=0∞∑i≤0≤m,j≤0≤c−m(−1)i∗Cmi∗Cc−mj∗(c−2i−2j)kk!∗xk\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^kk=0∑∞k!∑i≤0≤m,j≤0≤c−m(−1)i∗Cmi∗Cc−mj∗(c−2i−2j)k∗xk
但是这里我们不能忘记上面的分母和总情况2c,cn2^c,c^n2c,cn,最后剩下的是哪mmm种,还有一个CcmC_c^mCcm
所以最后第nnn项的系数就是Ccm2c∗cn∗∑i≤0≤m,j≤0≤c−m(−1)i∗Cmi∗Cc−mj∗(c−2i−2j)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}2c∗cnCcm∗i≤0≤m,j≤0≤c−m∑(−1)i∗Cmi∗Cc−mj∗(c−2i−2j)k
code
本来刚开始快速幂和组合数都不想开doubledoubledouble,但是同胞告诉我会炸intintint
#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,DB,DB,D是没有限制的,那么它们的生成函数则是普通型
1+x1!+x22!+x33!......=ex1+\frac{x}{1!}+\frac{x^2}{2!}+\frac{x^3}{3!}......=e^x1+1!x+2!x2+3!x3......=ex
而A,CA,CA,C的要求是必须出现偶数次,当然不出现也行,所以他们的生成函数就只能保留x偶数次x^{偶数次}x偶数次
1+x22!+x44!+......=ex+e−x21+\frac{x^2}{2!}+\frac{x^4}{4!}+......=\frac{e^x+e^{-x}}{2}1+2!x2+4!x4+......=2ex+e−x
所以先写T1的题解不是没有逻辑的
将四个的生成函数闭形式相乘来化简试试
(ex)2∗(ex+e−x2)2=e2x∗e2x+e−2x+24=e4x+2e2x+14(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}(ex)2∗(2ex+e−x)2=e2x∗4e2x+e−2x+2=4e4x+2e2x+1
然后还原成母函数
e4x=1+4x1!+(4x)22!+...(4x)nn!+....e^{4x}=1+\frac{4x}{1!}+\frac{(4x)^2}{2!}+...\frac{(4x)^n}{n!}+....e4x=1+1!4x+2!(4x)2+...n!(4x)n+....
2e2x=2(1+2x1!+(2x)22!+...+(2x)nn!+...)=2+4x1!+8x2!+...+2(2x)nn!+...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!}+...2e2x=2(1+1!2x+2!(2x)2+...+n!(2x)n+...)=2+1!4x+2!8x+...+n!2(2x)n+...
所以
e4x+2e2x+14=14∗(4+4x+2∗2x1!+(4x)2+2∗(2x)22!+...+(4x)n+2(2n)nn!)+...\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!})+...4e4x+2e2x+1=41∗(4+1!4x+2∗2x+2!(4x)2+2∗(2x)2+...+n!(4x)n+2(2n)n)+...
=14∗(4+(4+2∗2)x1!+(42+2∗22)x22!+...(4n+2∗2n)xnn!+...)=\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!}+...)=41∗(4+(4+2∗2)1!x+(42+2∗22)2!x2+...(4n+2∗2n)n!xn+...)
常数项是第零项,把14\frac{1}{4}41乘进去,所以第nnn项的系数应该是
14∗(4n+2∗2n)=4n−1+2n−1\frac{1}{4}*(4^{n}+2*2^{n})=4^{n-1}+2^{n-1}41∗(4n+2∗2n)=4n−1+2n−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+x1!+x22!+x33!+...+xnum[i]num[i]!i∈[1,n],1+\frac{x}{1!}+\frac{x^2}{2!}+\frac{x^3}{3!}+...+\frac{x^{num[i]}}{num[i]!}i∈[1,n],1+1!x+2!x2+3!x3+...+num[i]!xnum[i]
设dp[j]dp[j]dp[j]表示选了jjj个物品的排列数,那么对于第iii种物品而言,我们枚举该种物品选的个数是k∈[1,num[i]]k∈[1,num[i]]k∈[1,num[i]],那么排列数就要加上kkk个物品的排列数,但是因为它们是一种,顺序没有用,所以要除掉k!k!k!
于是可以写出转移方程式dp[j+k]=dp[j+k]+dp′[j]/k!dp[j+k]=dp[j+k]+dp'[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;elsedp1[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
题解
这道题跟上面第三题有点相似,我们可以先看对于nnn个字符,一共可以拼凑出n!n!n!个字符串
但是我们知道如果有两个AAA那么A0A1A_0A_1A0A1和A1A0A_1A_0A1A0我们是看不出来的,那么就应该排除掉它们的影响
所以这道题的公式就是n!A0!A1!...An!\frac{n!}{A_0!A_1!...A_n!}A0!A1!...An!n!
其实这道题的本质是卡我们的高精,哎~具体的代码理解在codecodecode里面有详解,你一定会看得懂的
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应该先写生成函数的学习博客,然后再写题解