【AtCoder Regular Contest 067 E】Grouping
动态规划。
题目大意:
有 $n$ 个人,编号 $1\sim n$,现在要将它们分组,满足:
- 每组的人数要在 $[A,B]$ 之间。
- 人数为 $i$ 的组要么没有,要么数量在 $[C,D]$ 之间。
求方案数。
题解:
考虑枚举组别以及选这个组别的人数,进行 dp。
令 $f_{i,j}$ 表示当前考虑到一组 $i$ 人,有 $j$ 个人已经分组了。
枚举 $k$ 表示一组 $i$ 人需要选 $k$ 组,不难得到转移:
由于 $i\cdot k$ 不超过 $D$,所以枚举 $k$ 是一个调和级数,故总时间复杂度 $O(n^2\log n)$。
Code:
#include<cstdio>
const int md=1e9+7,N=1007;
typedef long long LL;
int n,A,B,C,D,F[N][N],fac[N],iv[N];
inline int pow(int a,int b){
int ret=1;
for(;b;b>>=1,a=(LL)a*a%md)if(b&1)ret=(LL)ret*a%md;
return ret;
}
int main(){
scanf("%d%d%d%d%d",&n,&A,&B,&C,&D);
for(int i=*fac=1;i<=n;++i)fac[i]=(LL)fac[i-1]*i%md;
iv[n]=pow(fac[n],md-2);
for(int i=n-1;~i;--i)iv[i]=(i+1LL)*iv[i+1]%md;
F[A-1][0]=1;
for(int i=A;i<=B;++i)
for(int j=0;j<=n;++j){
int&f=F[i][j];f=F[i-1][j];
for(int k=C,g=pow(iv[i],C);k<=D&&k*i<=j;++k,g=(LL)g*iv[i]%md){
int L=n-j+k*i;
f=(f+(LL)F[i-1][j-k*i]*fac[L]%md*iv[k*i]%md*iv[L-k*i]%md*fac[k*i]%md*g%md*iv[k])%md;
}
}
printf("%d\n",F[B][n]);
return 0;
}