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
题解
首先我们由题意知道如果桌子上有两个颜色相同的巧克力,会被吃掉,所以最后桌子上的巧克力一定是互不相同的,也就是有 种不同的糖果留在上面,所以我们可以特判当 时,是无解的
当 时,脑子也想得出来是无解的,一共就 颗,你从哪里变出 颗
再来每次都是两颗两颗地吃,所以 也就是吃的颗数一定是偶数,所以也可以特判一下
接下来进入母函数板块
首先是
前面提到过最后剩在桌上的糖果一定都是互不相同的,剩
颗,也就是剩
种,那么这
种糖果拿的数量一定是奇数(最后剩下一个不能吃),剩下的
种糖果拿的一定是偶数(都被两两吃掉),如果你不能理解就算了 ,且吃的顺序不同算不同的方法加上是等概率,那么总情况就是
我们考虑最普通的指数型函数
奇数的糖果的话就要想办法消掉
,偶数的糖果就要想办法消掉
奇数项:
偶数项:
不要慌,我们来带一下
以奇数项为例,偶数项相信大家看完后会秒懂
那么把每一种糖果对应的母函数乘起来,即
运用二项式展开定理得到,看来我还得去学学这玩意儿
我们先把分母放一放,先只看分子,对其进行化简
将这个的封闭式展开,变成
但是这里我们不能忘记上面的分母和总情况
,最后剩下的是哪
种,还有一个
所以最后第
项的系数就是
code
本来刚开始快速幂和组合数都不想开
,但是同胞告诉我会炸
#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
题解
对于
是没有限制的,那么它们的生成函数则是普通型
而
的要求是必须出现偶数次,当然不出现也行,所以他们的生成函数就只能保留
所以先写T1的题解不是没有逻辑的
将四个的生成函数闭形式相乘来化简试试
然后还原成母函数
所以
常数项是第零项,把
乘进去,所以第
项的系数应该是
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
题解
我们对于每一种物品数,都可以写出其生成函数
设
表示选了
个物品的排列数,那么对于第
种物品而言,我们枚举该种物品选的个数是
,那么排列数就要加上
个物品的排列数,但是因为它们是一种,顺序没有用,所以要除掉
于是可以写出转移方程式
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
题解
这道题跟上面第三题有点相似,我们可以先看对于
个字符,一共可以拼凑出
个字符串
但是我们知道如果有两个
那么
和
我们是看不出来的,那么就应该排除掉它们的影响
所以这道题的公式就是
其实这道题的本质是卡我们的高精,哎~具体的代码理解在
里面有详解,你一定会看得懂的
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应该先写生成函数的学习博客,然后再写题解