题意:
定义一个排列的逆序数为排列中逆序对的个数。
给出
n
n
n和
k
k
k,问
n
n
n位数的所有排列中,逆序数为
k
k
k的排列个数有多少。
思路:
考虑动态规划
定义
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]:
i
i
i位数的排列中逆序数为
j
j
j的排列个数
考虑对于 n + 1 n+1 n+1位数的排列,可以看作是 ( n + 1 ) (n+1) (n+1)这个数插入到 n n n位数的排列中而形成。
那么对于
d
p
[
n
+
1
]
[
k
]
dp[n+1][k]
dp[n+1][k]的求取:
可以考虑利用
(
n
+
1
)
(n+1)
(n+1)一定大于
1
−
n
1-n
1−n中的任意一个数的性质。
初始
d
p
[
n
+
1
]
[
k
]
=
0
dp[n+1][k] = 0
dp[n+1][k]=0
假设第
(
n
+
1
)
(n+1)
(n+1)这个数插入到
n
n
n位数的排列最后一位,且最后新的排列的逆序数为
k
k
k
则:
d
p
[
n
+
1
]
[
k
]
=
d
p
[
n
+
1
]
[
k
]
+
d
p
[
n
]
[
k
]
dp[n+1][k] = dp[n+1][k] + dp[n][k]
dp[n+1][k]=dp[n+1][k]+dp[n][k]
若第
(
n
+
1
)
(n+1)
(n+1)这个数插入到
n
n
n位数的排列倒数第二位,因为
(
n
+
1
)
(n+1)
(n+1)一定是最大的数,故他跟他后面的数均会对答案有新的贡献,且贡献等于他后面的数的个数:
则:
d
p
[
n
+
1
]
[
k
]
=
d
p
[
n
+
1
]
[
k
]
+
d
p
[
n
]
[
k
−
1
]
dp[n+1][k] =dp[n+1][k] + dp[n][k-1]
dp[n+1][k]=dp[n+1][k]+dp[n][k−1]
…依此类推
则 d p [ n + 1 ] [ k ] = ∑ i = m a x ( 0 , k − n ) k d p [ n ] [ i ] dp[n+1][k] = \sum_{i=max(0,k-n)}^k dp[n][i] dp[n+1][k]=i=max(0,k−n)∑kdp[n][i]
其中求和式可以利用前缀和进行优化时间复杂度。
因多组数据,可以考虑离线处理,优化空间复杂度。
最后总复杂度为:
O
(
n
k
+
T
)
O(nk + T)
O(nk+T),
n
,
k
n,k
n,k为多组数据中的最大值。
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod = 1e9 + 7;
const int A = 2e4 + 10;
int dp[A],sum[A],Ans[A],Mx_n,Mx_k,q;
class Qu{
public:
int n,k,id;
bool operator<(const Qu& rhs) const{
if(n != rhs.n) return n < rhs.n;
return k < rhs.k;
}
}Q[A];
void solve(int n,int k){
dp[0] = 1;
int tot = 1;
for(int i=2 ;i<=n ;i++){
sum[0] = dp[0];
while(tot<=q && Q[tot].n == i && Q[tot].k == 0){
Ans[Q[tot].id] = 1;
tot++;
}
for(int j=1 ;j<=k ;j++) sum[j] = (sum[j-1] + dp[j])%mod;
for(int j=1 ;j<=k ;j++){
if(j-i+1 > 0) dp[j] = (sum[j] - sum[j-i])%mod;
else dp[j] = sum[j];
if(dp[j]<0) dp[j] += mod;
while(tot<=q && Q[tot].n == i && Q[tot].k == j){
Ans[Q[tot].id] = dp[j];
tot++;
}
}
}
//printf("tot = %d\n",tot);
for(int i=1 ;i<=q ;i++){
printf("%d\n",Ans[i]);
}
}
int main(){
scanf("%d",&q);
Mx_n = Mx_k = 0;
for(int i=1 ;i<=q ;i++){
scanf("%d%d",&Q[i].n,&Q[i].k);
Q[i].id = i;
Mx_n = max(Mx_n,Q[i].n);
Mx_k = max(Mx_k,Q[i].k);
}
sort(Q+1,Q+1+q);
solve(Mx_n,Mx_k);
return 0;
}