[poj1322]Chocolate——生成函数

题目大意:

一共有ccc种糖果,取nnn次,每次取到糖果种类都是等概率的,求有mmm种糖果个数为奇数个的概率。

思路:

直接概率DP时间复杂度太高,卡常数也不太好卡。

将每次取出来的糖果看成是一个带有重复元素的排列,直接计算复合条件的排列数量。

考虑符合条件的最后的序列的考虑EGF(指数型生成函数),可得出现次数为偶数次的糖果的生成函数为:
F0(x)=∑i=0∞x2i(2i)!=ex+e−x2 F_0(x)=\sum_{i=0}^{\infty}\frac{x^{2i}}{(2i)!}=\frac{e^x+e^{-x}}{2} F0(x)=i=0(2i)!x2i=2ex+ex
出现奇数次的糖果的生成函数为:
F1(x)=∑i=0∞x2i+1(2i+1)!=ex−e−x2 F_1(x)=\sum_{i=0}^{\infty}\frac{x^{2i+1}}{(2i+1)!}=\frac{e^x-e^{-x}}{2} F1(x)=i=0(2i+1)!x2i+1=2exex
可以得到最后的答案的生成函数为:
F1m(x)×F0c−m(x)=(ex−e−x2)m×(ex+e−x2)c−m F_1^{m}(x)\times F_0^{c-m}(x)=(\frac{e^x-e^{-x}}{2})^m\times(\frac{e^x+e^{-x}}{2})^{c-m} F1m(x)×F0cm(x)=(2exex)m×(2ex+ex)cm
exe^xex看成是一个整体,然后两边分别二项式展开之后做卷积,然后再将exe^xex展开即可得到第nnn项系数。
G(x)=2−c×∑i=0m(−1)i(mi)×e(m−2i)x×∑j=0c−m(c−mj)×e(2j−c+m)x=2(−c)×∑i=0m∑j=0c−m(−1)i(mi)(c−mj)×e(2m−2i+2j−c)x=2(−c)×∑i=0m∑j=0c−m(−1)i(mi)(c−mj)×∑k=0∞((2m−2i+2j−c)x)kk! \begin{aligned} G(x)&=2^{-c}\times \sum_{i=0}^{m}(-1)^i{m\choose i}\times e^{(m-2i)x}\times \sum_{j=0}^{c-m}{c-m\choose j}\times e^{(2j-c+m)x}\\ &=2^{(-c)}\times \sum_{i=0}^{m}\sum_{j=0}^{c-m}(-1)^i{m\choose i}{c-m\choose j}\times e^{(2m-2i+2j-c)x}\\ &=2^{(-c)}\times \sum_{i=0}^{m}\sum_{j=0}^{c-m}(-1)^i{m\choose i}{c-m\choose j}\times \sum_{k=0}^{\infty}\frac{((2m-2i+2j-c)x)^k}{k!} \end{aligned} G(x)=2c×i=0m(1)i(im)×e(m2i)x×j=0cm(jcm)×e(2jc+m)x=2(c)×i=0mj=0cm(1)i(im)(jcm)×e(2m2i+2jc)x=2(c)×i=0mj=0cm(1)i(im)(jcm)×k=0k!((2m2i+2jc)x)k
设第nnn项的系数为ana_nan,最后的答案为:
an×(cm)×n!2c×cn \frac{a_n\times{c\choose m}\times n!}{2^c\times c^n} 2c×cnan×(mc)×n!
垃圾poj
坑点:
如果用实数类去计算,中间过程可能会爆精度,特别是中间的快速幂,建议将最后分母的那个cmc^mcm移到系数的快速幂里面,这样会让中间过程的数值小一些。
好像用不了%lf,直接用%f即可。
交C++好像过不了。

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.1
 * Problem : Chocolate
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("poj1322.in","r",stdin);
    freopen("poj1322.out","w",stdout);
}

template<typename T>void read(T &_){
	_=0; T fl=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
	for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0');
	_*=fl;
}

const int maxn=1e6+10;
const int maxc=200+10;
int c,n,m;
double C[maxc][maxc],ans;

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

void init(){
	C[0][0]=1;
	REP(i,1,200){
		C[i][0]=1;
		REP(j,1,i)C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
}

int main(){
    File();
	init();
	while(~scanf("%d%d%d",&c,&n,&m)){
		if(!c)break;
		ans=0;
		if(m>c || m>n || (n-m)%2){
			printf("0.000\n");
			continue;
		}
		REP(i,0,m)REP(j,0,c-m)
			ans+=(i%2 ? -1 : 1)*C[m][i]*C[c-m][j]*qpow((2*m-2*i+2*j-c)*1.0/c,n);
		ans=ans*C[c][m]/qpow(2,c);
		printf("%.3f\n",ans);
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值