CF_402F dp+组合数学
题目链接:http://codeforces.com/problemset/problem/403/D
/**算法分析: 这道题综合的考察了dp背包思想和组合数学 */ #include<bits/stdc++.h> #define MAXN 1050 #define PI acos(-1.0) #define MOD 1000000007 #define REP(i,n) for(int i=0; i<n; i++) #define FOR(i,s,t) for(int i=s; i<=t; i++) #define mem(a,b) memset(a,b,sizeof(a)) #define show(x) { cerr<<">>>"<<#x<<" = "<<x<<endl; } #define showtwo(x,y) { cerr<<">>>"<<#x<<"="<<x<<" "<<#y<<" = "<<y<<endl; } using namespace std; int n,k; int dp[MAXN][55]; //dp[i][j]表示把i分成j个不同的数的方案数 int C[MAXN][55]; //组合数 long long fact[55]; //阶乘 void init_dp() { mem(dp,0); dp[0][0] = 1; //用0,1背包思想:数字i看成体积为i的物品,放到总体积为n的容器内的方案数 //实际的dp: dp[d][i][j];但考虑的数的是d是,能够用j个不同的数凑出i的方案数,第一维被优化了 //转移方程:dp[d][i][j] = dp[d-1][i][j] + dp[d-1][i-d][j-1],分为用与不用d这个数 for(int d=1; d<MAXN; d++) for(int i=MAXN-1; i>=d; i--) for(int j=min(d+1,50); j>=1; j--) dp[i][j] = (dp[i][j] + dp[i-d][j-1])%MOD; mem(C,0); for(int i=0; i<MAXN; i++) { C[i][0] = 1; for(int j=1; j<=min(i,50); j++) C[i][j] = (C[i-1][j]+C[i-1][j-1])%MOD; } fact[0] = 1; for(int i=1; i<55; i++) fact[i] = fact[i-1]*i%MOD; } int main() { //freopen("E:\\acm\\input.txt","r",stdin); init_dp(); int T; cin>>T; while(T --) { int n,k; scanf("%d %d",&n,&k); int m = k*(k+1)/2; if(n < m) printf("0\n"); else { long long ans = 0; for(int i=m; i<=n; i++) ans = (ans + (long long)C[n-i+k][k]*dp[i][k])%MOD; printf("%I64d\n",ans*fact[k]%MOD); } } }