【问题描述】 | ||
将自然数 1 到 n 任意排列,然后在排列的每两个数之间根据他们的大小关系插入“ >”和“ <”。例如:对于 1..5 的一个排列: 3 2 4 1 5,可得到: 3 > 2 < 4 > 1 < 5,其中有两个“ >” 和2 个“ <”。 现在给出自然数 n, 问在自然数 1..n 的所有排列中,有多少个排列恰好有 k 个“ <”。 请你解答这个问题。 |
【输入格式】 | |||
包含多组数据。第一行一个整数 T,表示有 T 组数据。 每组数据的占一行,包含两个整数 n 和 k,它们之间用一个空格分开。 |
【输出格式】 | |||
共 T 行, 每组数据输出一行,每行一个整数,表示对应输入的排列数,这个数如果很大,则需要输出 mod 1000000007 的结果。 |
【输入样例】 | |||
4 5 2 8 1 9 4 7 3 |
【输出样例】 | |||
66 247 156190 2416 |
【数据范围】 | |||
对于 30%的数据: n<=10 对于 100%的数据: k<n<=1000 |
分析:
看数据k<=1000 n<=1000很容易能想到用动规
本着求啥设啥的懒人原则
设f i,j 为 放第i个数时满足j个“<”关系的方案数
接下来是f i,j 的状态分析:
对于一个新数i的放入 (第i个数是当前最大的数)
那么考虑将i放入原先由 < 连接和 > 连接的两数之间
情况1(注意:需要考虑最初位置,第一个数与位置0):
插入新数i并不会改变原有的 < 关系
那么这样的选择有: 原来的 j 个 加上 将i放在所有数之前的抉择
也就是 t1=f i-1,j * (j+1) ;
情况2:(相似的,最后一个位置也应当考虑)
插入新数i会新增一个 < 关系
选择有:i-1-j 个的原有 > 关系(i个数提供 i-1 个关系其中j个为<)加上 放在最末的抉择
也就是 t2= f i-1,j-1 * (i-j)
最后 f i,j =t1 + t2;
/* f(i,j) 第i个数满足j个"<"关系的方式 */ #include<bits/stdc++.h> using namespace std; const int mod=1000000007; int t; int n,k; long long f[1005][1005]; void re() //毕竟鬼知道T有多大,为了你我安全而打表 { memset(f,0,sizeof(f)); f[0][0]=1; for(int i=1;i<=1000;i++) for(int j=min(i,1000);j>=0;j--) { f[i][j]=f[i-1][j]*(j+1)+f[i-1][j-1]*(i-j); f[i][j]%=mod; } return ; } void solve() { scanf("%d%d",&n,&k); cout<<f[n][k]<<endl; return ; } int main(){ re(); scanf("%d",&t); for(int i=1;i<=t;i++) solve(); return 0; }