排列组合
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Problem Description
有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题解:指数型母函数,对于每一个元素,可以写成(1+x+x^2/2!+x^3/3!......),然后乘起来,最后的系数再除以m的阶乘就是答案,ans=x^m/m!的系数。#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; typedef long long ll; ll n,m,a[20]; struct node{ ll zi,mu; }e; ll fac(ll n){ if(n==1||n==0)return 1; return n*fac(n-1); } node add(node a,node b){ a.zi*=b.mu; b.zi*=a.mu; a.mu*=b.mu; a.zi+=b.zi; ll t=__gcd(a.zi,a.mu); a.zi/=t; a.mu/=t; return a; } void dfs(ll now,ll sum,ll cur){ if(sum==m){ e=add(e,(node){1,cur});//把系数加起来 return; } if(now>n)return; ll i; for(i=0;i<=a[now];i++){ if(sum+i<=m)dfs(now+1,sum+i,cur*fac(i));//如果当前能继续放这个元素 else break; } } int main(){ while(scanf("%lld%lld",&n,&m)!=EOF){ ll i,j; for(i=1;i<=n;i++)scanf("%lld",&a[i]); e.zi=0; e.mu=1; dfs(1,0,1); printf("%lld\n",e.zi*fac(m)/e.mu);//答案要乘m! } return 0; }