版本1:
有方程a1*x1+a2*x2+...an*xn=N,给定n(1000=>n>=1)个系数ai(1000>=ai>=0)和N(1000>=N>0),求满足这个方程的非负整数解(x1,x2...xn)的个数。(结果对10007取模)
实现1:
构造母函数,(1+x^1+x^2+..x^(N/a1))^^a1*(1+x^1+x^2+..x^(N/a2))^^a2...(1+x^1+..x^(N/an))^^an
^^ai表示从从第一项中取出的项指数乘以ai;
这个函数展开后x^N前面的系数就是方程解的个数。
时间复杂度O(N^2*n);
实现2:
DP,f[N]+=f[N-a[i]] (1<=i<=n)
时间复杂度O(N*n)
版本2:
求方程x1+x2+…...+xn = m满足xi>=1的解的个数。
输入由多组数据组成。每组数据输入一行n和m(1<=n<=300,n<=m<=100000)。
由于方程的解的个数会超过整数范围,因此输出的结果对10007求余
实现1:
由于是上例的特殊情况,令a[i]都=1;
直接套用上例DP时间复杂度为O(m*n),可以接受;
套用母函数时间复杂度为O(m^2*n),速度太慢;
实现2:
用组合数学中的隔板法,直接得结果C(n-1,m-1);
计算C(n-1,m-1)也有两种解法:
1.C(n,m)=C(n,m-1)+C(n-1,m-1);
可以递推,但是m很大消耗内存很大,但可以用滚动数组优化,时间复杂度O(m*n);
2.C(n,m)=m*(m-1)*..(m-n+1)/n!
先令a[1...n]=1..n;a[m-n+1..m]=m-n+1..m;
可以先将a[1]到a[n]这n个数每个数与a[m-n+1]到a[m]这n个数的最大公约数都约掉,因为C(n,m)是整数,所以
a[1]到a[n]这n个数必然都能完全被约掉,到后面都变为1,最后将a[m-n+1]到a[m]这n个数累乘,乘的同时可以取
模取掉;
时间复杂度O(n^2),跟m无关了,最优!!
附上这种方法的代码:
#include<iostream> using namespace std; int n,m,i,j,a[100005]; int gcd(int a,int b) { if (a%b==0) return b; else return gcd(b,a%b); } int main() { int ans,t,k; while(cin>>n>>m) { //C(n-1,m-1) ans=1; for(i=m-n+1;i<=m-1;i++) a[i]=i; for(i=1;i<=n-1;i++) { t=i; for(j=m-n+1;j<=m-1;j++) { if (t==1) break; k=gcd(a[j],t); a[j]=a[j]/k; t=t/k; } } for(i=m-n+1;i<=m-1;i++) ans=ans*a[i]%10007; cout<<ans<<endl; } }