题目
Problem Description
- 有一群有着不一样ID的人站成了一排。如果某个人他的前面或者后面的人的ID比他大,那么他是开心的。
- 比如,一行里面有5个人,他们的ID分别为1,3,2,5,4,这时候ID是1,2,4的人是开心的,ID是3,5的人是不开心的。
- 这里一共有n!种不同的方式去产生n个人的排列,你需要解决q个询问,每个询问包含n和k,你需要求n个数的排列里至少有k个开心的人的排列数,由于答案可能很大,所以要将答案对10^9+7取模。
Input
第一行一个整数q,代表询问的组数。
接下来q行每行两个整数n和k。1<=q<=100
1<=n<=3000
1<=k<=nOutput
对于每个询问,一行输出n个数的排列里至少有k个开心的人的方案数,对10^9+7取模。
Sample Input
3
3 2
4 3
10 7Sample Output
4
8
1433856Hint
对于样例1,n=3,k=2时符合的方案为{1,2,3}{1,3,2}{2,3,1},{3,2,1}.
分析
- 题目中要求至少有k个开心的人,我们可以转换为至多有n-k个不开心的人,于是就变成问对于1到n这n个数,有几种排列使得至多有n-k个数,它左右的数都比他小(可以把它想象为一个山峰)。
- 于是可以用dp来做,由
[1,i-1]
这些数满足的排列数推出[1,i]
满足的排列数。 - 每次是多了一个数,且它是最高的(即不管放到哪都会产生一个山峰),在分两类
- 加了这个数山峰数不变,那么它就肯定加在某个原有山峰的旁边(相邻处)。
- 加了这个数山峰数多了1,那么它肯定不能加在原有山峰的旁边。
- 于是就有了dp方程
dp[i,j]= //代表 1~i 满足有 j 个山峰的排列数
dp[i-1,j]*j*2 //加了i山峰数不变
+dp[i-1,j-1]*(i-(j-1)*2) //加了i山峰数多了1
- 最后求出 ∑n−ki=1dp[n][i] 即可
程序
#include <cstdio>
#define Ha 1000000007
int n,k,q;
long long dp[3005][3005],ans;
int main(){
dp[1][1]=1;
for (int i=2; i<=3000; i++)
for (int j=1,kk=((i%2==0) ? i/2 : i/2+1); j<=kk; j++)
dp[i][j]=(dp[i-1][j]*j*2+dp[i-1][j-1]*(i-(j-1)*2))%Ha;
for (q=(scanf("%d",&q),q); q; q--,ans=0){
scanf("%d%d",&n,&k);
for (int i=1; i<=n-k; i++) ans=(ans+dp[n][i])%Ha;
printf("%lld\n",ans);
}
return 0;
}
提示
- 做dp题的时候,遇到“至少”的条件时,可以考虑将其转换为“至多”来做,可能更直观一些。(其实用至少来做也行,这道题也是一样,不过要稍微绕一下,没这么好写)