BZOJ 3622 : 已经没有什么好害怕的了(dp + 广义容斥原理)

今天没听懂 h10 的讲课 但已经没有什么好害怕的了

题意

给你两个序列 a,b 每个序列共 n 个数 , 数之间两两不同

ab 之间有多少配对方案 使得 ai>bi 的对数 比 bi>ai 的恰好多 k 对.

(1kn2000)

题解

首先这个对数多的有点恶心 , 我们直接转化成 ai>bi 的共有 n+k2 对 (自行模拟一下...)

然后不是整数的时候答案就是 0 ... 这个我没有判断 , 但过了...

然后我们还是用 k 表示 ai>bi 的对数 .

看到 恰好 我们不难想到 至少 . 我们考虑如何计算至少 k 对的贡献 .

因为这有一个鬼畜的定理...

广义容斥定理 (二项式反演?) :

bk=i=kn(ik)ai

ak=i=kn(1)ik(ik)bi

这个 h10 讲了证明... 感性理解然后记忆一下吧...

不难想到一个简单的 dp 我们令 dp[i][j]a 中前 i 个数能选出比 b 大共有 j 对的方案数.

然后我们记 j=1n[ai>bj]=ti . 也就是 ai 比 多少个 bj

dp[i][j]=dp[i1][j]+dp[i1][j1]×(tij+1)

dp[i1][j] 就是上一个同样 j 对时候 此时可以直接转移

dp[i1][j1]×(tij+1) 这个就是你当前已选了 j1 对 , 当前还剩下 tij+1 个可选

可以这样理解 前者是当前不选时候的贡献 后者是选择时候的贡献

然后此处我们不难发现 a 必须都要是有序的 不然会算错

DOFY : 排序是为了方便计数....不然你计不了答案...

就是你之前选的方案的 ti 应该被后面给包括了 就是后面选方案的时候要算上当前的方案

然后我们令 Fi 为至少有 i 对满足 a>b 的方案数.

Fi=fn,i×(ni)!

然后令恰好有 k 对满足的 Gk 就是

Gk=i=kn(1)ik(ik)Fi

然后代码就比较好写咯...

代码

/************************************************************** Problem: 3622 User: zjp_shadow Language: C++ Result: Accepted Time:2184 ms Memory:64488 kb ****************************************************************/ #include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("3622.in", "r", stdin); freopen ("3622.out", "w", stdout); #endif } typedef long long ll; const int N = 2010; const ll Mod = 1e9 + 9; int n, k, a[N], b[N], t[N]; inline void Add(ll &a, ll b) { if ((a += b) >= Mod) a -= Mod; } ll dp[N][N], F[N], G[N], fac[N], C[N][N]; void Init(int maxn) { fac[0] = 1; For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod; C[0][0] = 1; For (i, 1, maxn) { C[i][0] = 1; For (j, 1, maxn) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod; } } int main () { File(); Init(2000); n = read(); k = read(); k = (n + k) >> 1; For (i, 1, n) a[i] = read(); For (i, 1, n) b[i] = read(); sort(a + 1, a + 1 + n); dp[0][0] = 1; For (i, 1, n) { For (j, 1, n) if (a[i] > b[j]) ++ t[i]; For (j, 0, n) { Add(dp[i][j], dp[i - 1][j]); if (!j) continue ; Add(dp[i][j], dp[i - 1][j - 1] * max(0, (t[i] - j + 1)) % Mod); } } For (i, 1, n) F[i] = dp[n][i] * fac[n - i] % Mod; For (i, k, n) { G[k] += ((i - k) & 1 ? -1 : 1) * (C[i][k] * F[i] % Mod); G[k] = (G[k] % Mod + Mod) % Mod; } printf ("%lld\n", G[k]); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/8665950.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(748)  评论(1编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示