[山东神秘题]逆序对
求长度为 的逆序对数为 的排列的个数。
Sol:
的dp是显然的,或许能多项式优化,不过有更简单的做法。
我们设 表示以 为右端点的逆序对个数,显然一组 唯一对应一个序列。我们要求 。
考虑容斥,我们假设有 个位置不满足第一个限制,显然 最大是 的。考虑设 表示从 中选 个数和为 的方案,直接插板法就能得出答案。我们发现这个状态不太好转移。
考虑这样来枚举选出的集合:维护一个递减数列,每次操作对所有数都加一,操作结束后可以选择是否在末尾增添一个 。
再令 表示在末尾增添 次 后和为 的方案数(注意数列里的数不能超过 ),我们有:
#include <bits/stdc++.h>
using namespace std;
const int N = 200005, mod = 1e9 + 7;
int n, k, fac[N], ifac[N];
inline int power(int a, int b) {
int t = 1, y = a, k = b;
while (k) {
if (k & 1) t = (1ll * t * y) % mod;
y = (1ll * y * y) % mod;
k >>= 1;
} return t;
}
inline int C(int n, int m) {
return 1ll * fac[n] * (1ll * ifac[m] * ifac[n - m] % mod) % mod;
}
int f[505][N];
int main() {
cin >> n >> k;
fac[0] = 1; for (int i = 1; i <= n + k; ++i) fac[i] = 1ll * i * fac[i - 1] % mod;
ifac[n + k] = power(fac[n + k], mod - 2);
for (int i = n + k - 1; ~i; --i) ifac[i] = (1ll * (i + 1) * ifac[i + 1]) % mod;
f[0][0] = 1;
for (int i = 1; i <= 500; ++i) {
for (int j = i; j <= k; ++j) {
f[i][j] = f[i][j - i] + f[i - 1][j - i];
if (f[i][j] >= mod) f[i][j] -= mod;
if (j > n) {
f[i][j] -= f[i - 1][j - n - 1];
if (f[i][j] < 0) f[i][j] += mod;
}
}
} int ans = 0;
for (int i = 0; i <= k; ++i) {
int del = 0;
for (int j = 0; j <= 500; ++j) {
if (j & 1) {
del -= f[j][i];
if (del < 0) del += mod;
} else {
del += f[j][i];
if (del >= mod) del -= mod;
}
} ans += 1ll * del * C(n + k - i - 1, n - 1) % mod;
if (ans >= mod) ans -= mod;
} printf("%d\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具