luogu P1521 题解
一开始可以把排逆序对看成从小到大插入元素, 我们令 \(a_i\) 表示插入元素 \(i\) 所产生的逆序对数量。
可以推出
考虑容斥
我们令 \(sum\) 表示选出的不满足的位置逆序的的和, \(t\) 表示选出了多少个数不合法, 可以发现, 通过插板法对答案的贡献为 \((-1)^{t} \cdot C_{k + n - 1 - sum}^{n - 1}\)。
显然, \(\frac{t \cdot (t + 1)}{2} \le sum, t \le \sqrt{2sum}\)
考虑变换一下要求的东西
对于以上信息, 只需要求出对于两两不同的数组 \((b_1, b_2 \dots b_k), 1 \le b_1 < b_2 < \dots < b_k \le n, \sum b_i = sum\), 的方案数为 \(x\), 则贡献是 \((-1)^k \cdot C_{k + n - 1 - sum}^{n - 1}\), 这种发案相当于是 \(b_1, b_2, \dots b_k\) 都不合法。
考虑 DP, 状态 \(f_{i, j}\) 表示选了 \(i\) 个数, \(a_i\) 的和为 \(sum\) 时的方案数。
若选的元素都 $ > 1$, 则可以把选的数都 -1 的方案再全部 +1 即可回到这个方案, 所以答案为 \(f_{i, j - i}\), 若答案里有可能包含 \(n + 1\), 方案数为 \(f_{i - 1, j - (n + 1)}\), 需要减去这一部分的代价。
若选了元素存在 \(1\), 只需要先把 \(1\) 删到, 即 \(f_{i - 1, j - 1}\), 这里面还不能包含元素 \(1\), 所以答案应为 \(f_{i - 1, j - 1 - (i - 1)}\)
所以 \(f_{i, j} = f_{i - 1, j - i} + f_{i, j - i} - f_{i - 1, j - (n + 1)}\)
时间复杂度 \(O(k \sqrt{k} + n)\)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, mod = 1e9 + 7;
int n, k, dp[505][N], g[N], f[N], inv[N], ans;
int C(int n, int m){
return 1ll * f[n] * g[m] % mod * g[n - m] % mod;
}
int main(){
cin >> n >> k;
f[0] = g[0] = inv[1] = 1;
for(int i = 1; i <= 200000; ++i){
f[i] = 1ll * f[i - 1] * i % mod;
if(i > 1){
inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
}
g[i] = 1ll * g[i - 1] * inv[i] % mod;
}
dp[0][0] = 1;
ans = C(k + n - 1, n - 1);
for(int i = 1; i <= 500; ++i){
for(int j = 0; j <= k; ++j){
if(j - i >= 0){
dp[i][j] = (dp[i - 1][j - i] + dp[i][j - i]) % mod;
}
if(j - (n + 1) >= 0){
dp[i][j] = (dp[i][j] - dp[i - 1][j - (n + 1)] + mod) % mod;
}
if(k + n - 1 - j >= n - 1){
ans = (ans + 1ll * (i % 2 ? -1 : 1) * dp[i][j] * C(k + n - 1 - j, n - 1) % mod + mod) % mod;
}
}
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号