ZOJ-3380 Patchouli’s Spell Cards DP, 组合计数
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3380
题意:有m种不同的元素,每种元素都有n种不同的相位,现在假设有每种元素各一个,其相位是等概率随机的。如果几个元素的相位相同,那么帕琪就可以把它们组合发动一个符卡(Spell Card)。现在问帕琪能够发动等级不低于l,即包含l个相同相位的不同元素的附卡的概率。
首先所有的总数是n^m,然后只要求满足情况的数目了,对于 l >m/2我们可以直接用组合数来求的,即n*Σ( C(m,i)*(n-1)^(m-i) ),如果 l <= m/2就麻烦一些了,因为这里存在重复的情况,组合数求比较麻烦,于是我们可以用DP,f[i][j]表示前 i 个数放在 j 个位置并且相同的元素个数小于 l 的数目,f[i][j]=sum{dp[i-1][j-k]*choose[m-(j-k)][k] | k≤j && k<l}。然后这题大数,直接用Java了。。。
上面是O(nml)的复杂度,还有一个O(mml)的:
dp[i,j] 表示用i个数字在m个里放了j个位置,i表示有i个,不一定是[1,i]
dp[i,j] = ∑ dp[i-1,j-k]*C[m-(j-k),k]*(n-(i-1)) 1≤k≤j , k<l
最后答案为 ∑ dp[i,m]/i! 刚才用到的是乘法原理,是排列,要转化为组合数!
1 //STATUS:Java_AC_1240MS_20194KB 2 import java.util.*; 3 import java.math.*; 4 import java.io.*; 5 import java.text.*; 6 7 public class Main { 8 static final int N=101; 9 static BigInteger[][] f=new BigInteger[N][N]; 10 static BigInteger[][] C=new BigInteger[N][N]; 11 public static void main(String args[]){ 12 Scanner cin = new Scanner (new BufferedInputStream(System.in)); 13 int i,j,k; 14 for(i=0;i<N;i++)C[i][0]=C[i][i]=BigInteger.valueOf(1); 15 for(i=2;i<N;i++){ 16 for(j=1;j<i;j++) 17 C[i][j]=C[i-1][j-1].add(C[i-1][j]); 18 } 19 int n,m,l; 20 BigInteger tot,cnt; 21 while(cin.hasNext()) 22 { 23 m=cin.nextInt(); 24 n=cin.nextInt(); 25 l=cin.nextInt(); 26 if(l>m){ 27 System.out.println("mukyu~"); 28 continue; 29 } 30 tot=BigInteger.valueOf(n); 31 tot=tot.pow(m); 32 cnt=BigInteger.ZERO; 33 if(l>m/2){ 34 for(i=l;i<=m;i++){ 35 cnt=cnt.add(C[m][i].multiply( BigInteger.valueOf(n-1).pow(m-i) )); 36 } 37 cnt=cnt.multiply(BigInteger.valueOf(n)); 38 } 39 else { 40 for(i=0;i<=n;i++) 41 for(j=0;j<=m;j++)f[i][j]=BigInteger.ZERO; 42 f[0][0]=BigInteger.ONE; 43 for(i=1;i<=n;i++){ 44 for(j=0;j<=m;j++){ 45 for(k=0;k<=j && k<l;k++){ 46 f[i][j]=f[i][j].add(f[i-1][j-k].multiply(C[m-j+k][k])); 47 } 48 } 49 } 50 cnt=tot; 51 cnt=cnt.subtract(f[n][m]); 52 } 53 BigInteger t=cnt.gcd(tot); 54 cnt=cnt.divide(t); 55 tot=tot.divide(t); 56 57 System.out.println(cnt+"/"+tot); 58 } 59 } 60 }