MIN-MAX 反演

我们知道对于普通的 \(\min-\max\) 容斥有如下式子:

\[\max(S) = \sum_{T \subseteq S} (-1)^{|T| + 1} \min(T)\\
\min(S) = \sum_{T \subseteq S} (-1)^{|T| + 1} \max(T)
\]

证明可以构造一一映射,抵消贡献。

上述式子在期望意义下也是成立的:

\[E[\max(S)] = \sum_{T \subseteq S} (-1)^{|T| + 1} E[\min(T)]
\]

证明可以考虑期望的线性性。


Kth MIN-MAX 反演

本文参考了 张俊逸同学 的集训讲解。

构造容斥系数

我们可以推广到求第 \(k\) 大元素上来。

我们尝试构造容斥系数 \(f\) ,满足:

\[kth \max(S) = \sum_{T \subseteq S} f_{|T|} \min(T)
\]

然后来考虑一下对于集合 \(T\) 中第 \(i\) 大的元素,如果 \(\min(T)\) 等于这个元素,那么只有比它大的 \(i-1\) 个元素是可能存在的,然后我们考虑 \(T\) 什么时候才能对于答案进行贡献,其实就是要满足 \([i = k]\) 。

那么表达出来就是

\[\sum_{j = 1}^{i - 1} {i - 1 \choose j} f_{j + 1} = [i = k]
\]

二项式反演

这个形式我们可以套用二项式反演(广义容斥定理)

原来我博客提及的都是从 \(i = k \to n\) 的,其实 \(i = 1 \to k\) 也是一样的。

二项式反演:

假设数列 \(f\) 和 \(g\) 满足:

\[g_i = \sum_{j = 1}^{i} {i \choose j} f_j
\]

那么就有

\[f_i = \sum_{j = 1}^{i} (-1)^{i - j} {i \choose j} g_j
\]

可以考虑生成函数证明,设 \(f_0 = g_0 = 0\) 。

前者就为 \(G = F * e^x\) 后者为 \(F = G * e^{-x}\) ,显然是等价的。

那么对于前面那个式子,我们设 \(g_{i - 1} = [i = k], f'_j = f_{j + 1}\) 。

那么根据二项式反演就可以得到

\[f'_{i} = \sum_{j = 0}^{i} (-1)^{i - j} {i \choose j} g_j
\]

\[f_{i + 1} = (-1)^{i - (k - 1)} {i \choose k - 1}
\]

随意整理一下就有

\[f_i = (-1)^{i - k} {i - 1 \choose k - 1}
\]

结论

最终我们就可以如下求 \(kth \max\)

\[kth \max(S) = \sum_{T \subseteq S} (-1)^{|T| - k} {|T| - 1 \choose k - 1} \min(T)
\]

不难发现只有元素个数 \(\ge k\) 的子集是有用的。

例题

Luogu P4707 重返现世

题意

给定集合 \(S\) 中每个元素出现的概率 \(\displaystyle \frac{p_i}{m}\) ,其和为\(1\) ,每次会按概率出现,每次会按概率出现一个元素。

求出现 \(k\) 个元素的期望次数。

设 \(|S| = n\) 满足限制

\(n \le 1000, \left| n - k \right| \le 10, 1 \le \sum p = m \le 10000\)

题解

出现 \(k\) 个元素的期望次数等价于出现时间第 \(n-k+1\) 晚的数出现的期望次数。

那么套用 \(kth \max\) 容斥后,我们相当于要求每个集合出现最早的数的期望次数 \(E(T)\) 。

参考 此处 就可以得到

\[E(T) = \frac{1}{\sum_{i \in T} \frac{p_i}{m}}
\]

整理一下

\[E(T) = \frac{m}{\sum_{i \in T} p_i}
\]

如果 \(|S| \le 20\) 那么就可以直接做了,可是这题数据范围有点大,不太好做。

但是我们发现 \(m\) 不大,那么我们可以对于每个 \(\sum_{i \in T} p_i\) 求得其系数解决。

设 \(g_{i, j}\) 为集合大小为 \(i\) ,概率和为 \(j\) 的方案数,但是这样的话直接 \(dp\) 复杂度是 \(O(nmk)\) 的,我们显然不能这样做。

我们需要把容斥系数也要考虑到一起 \(dp\) 。

设 \(f_{i, j}\) 表示当前选定的集合的 \(p\) 的和为 \(i\) ,组合数下面那里 \(k=j\) 。

对于当前物品而言,只有两种选择:要么加入集合,要么不加入。

  • 不加入,直接转上去 \(f_{i, j} \to f'_{i, j}\) 。

  • 加入这个元素,考虑其贡献。

    \(f\) 的形式为 \(\displaystyle f_{j - p, k - 1} = \sum_i (-1)^{i - (k - 1)} {i - 1 \choose k - 2} g_{i, j - p}\) 。

    那么就会导致集合大小强制多 \(1\) ,并且概率和变成 \(j\) ,那么就是 \(\displaystyle \sum_i (-1)^{i-k}{i\choose k-1} g_{i, j - p}\) 。

    接下来 强行 凑组合数递推形式 \(\displaystyle {n\choose m}={n-1\choose m}+{n-1\choose m-1}\) ,把 \(\displaystyle {i \choose k - 1}\) 变为 \(\displaystyle {i \choose k}\)

    那么我们要减去的其实就是 \(\displaystyle -\sum(-1)^{i-k}{i-1\choose k-1} g_{i, j - p}\) ,其实就是 \(- f_{j - p, k}\) 。

那么最后的转移就是 \(f'_{i,j} + f'_{i - p, j - 1} - f'_{i - p, j} \to f_{i, j}\) 。

注意要滚动第一维,不然开不下。

复杂度是 \(O(n(n - k)m)\) ,还是很暴力。

后面这些强行套的地方好没有意思啊!

代码

#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 Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("P4707.in", "r", stdin);
freopen ("P4707.out", "w", stdout);
#endif
} const int N = 1010, Mod = 998244353; int n, K, m, dp[2][10010][13]; inline int fpm(int x, int power) {
int res = 1;
for (; power; power >>= 1, x = 1ll * x * x % Mod)
if (power & 1) res = 1ll * res * x % Mod;
return res;
} inline void Add(int &x, int y) {
if ((x += y) >= Mod) x -= Mod;
} int main () { File(); n = read(); K = n - read() + 1; m = read(); For (i, 1, K) dp[0][0][i] = Mod - 1; int cur = 0;
For (i, 1, n) {
int p = read();
For (j, 0, m) For (k, 0, K) if (dp[cur][j][k]) {
Add(dp[cur ^ 1][j][k], dp[cur][j][k]);
Add(dp[cur ^ 1][j + p][k + 1], dp[cur][j][k]);
Add(dp[cur ^ 1][j + p][k], Mod - dp[cur][j][k]);
dp[cur][j][k] = 0;
}
cur ^= 1;
} int ans = 0; For (i, 1, m)
ans = (ans + 1ll * m * fpm(i, Mod - 2) % Mod * dp[cur][i][K]) % Mod; printf ("%d\n", ans); return 0; }

Kth MIN-MAX 反演的更多相关文章

  1. 在一定[min,max]区间,生成n个不重复的随机数的封装函数

    引:生成一个[min,max]区间的一个随机数,随机数生成相关问题参考→链接 var ran=parseInt(Math.random()*(max-min+1)+min); //生成一个[min,m ...

  2. LINQ to SQL Count/Sum/Min/Max/Avg Join

    public class Linq { MXSICEDataContext Db = new MXSICEDataContext(); // LINQ to SQL // Count/Sum/Min/ ...

  3. 2.10 用最少次数寻找数组中的最大值和最小值[find min max of array]

    [本文链接] http://www.cnblogs.com/hellogiser/p/find-min-max-of-array.html [题目] 对于一个由N个整数组成的数组,需要比较多少次才能把 ...

  4. LINQ Count/Sum/Min/Max/Avg

    参考:http://www.cnblogs.com/peida/archive/2008/08/11/1263384.html Count/Sum/Min/Max/Avg用于统计数据,比如统计一些数据 ...

  5. 【转载】:【C++跨平台系列】解决STL的max()与numeric_limits::max()和VC6 min/max 宏冲突问题

    http://www.cnblogs.com/cvbnm/articles/1947743.html 多年以前,Microsoft 幹了一件比 #define N 3 還要蠢的蠢事,那就是在 < ...

  6. LINQ to SQL 语句(3) 之 Count/Sum/Min/Max/Avg

    LINQ  to SQL 语句(3) 之  Count/Sum/Min/Max/Avg [1] Count/Sum 讲解 [2] Min 讲解 [3] Max 讲解 [4] Average 和 Agg ...

  7. [转]LINQ语句之Select/Distinct和Count/Sum/Min/Max/Avg

    在讲述了LINQ,顺便说了一下Where操作,这篇开始我们继续说LINQ语句,目的让大家从语句的角度了解LINQ,LINQ包括LINQ to Objects.LINQ to DataSets.LINQ ...

  8. 动态规划——min/max的单调性优化总结

    一般形式: $max\{min(ax+by+c,dF(x)+eG(y)+f)\},其中F(x)和G(y)是单调函数.$ 或 $min\{max(ax+by+c,dF(x)+eG(y)+f)\},其中F ...

  9. Hive函数:SUM,AVG,MIN,MAX

    转自:http://lxw1234.com/archives/2015/04/176.htm,Hive分析窗口函数(一) SUM,AVG,MIN,MAX 之前看到大数据田地有关于max()over(p ...

  10. 产生10个随机数5-9之间 统计一个int类型的一维数组中有多少个在[min,max]之间的数

    * 产生10个随机数5-9之间 统计一个int类型的一维数组中有多少个在[min,max]之间的数 */ import java.util.*; public class Demo{ public s ...

随机推荐

  1. 【面向对象设计原则】之开闭原则(OCP)

    开闭原则是面向对象设计的一个重要原则,其定义如下: 开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭.即软件实体应尽量在不修改原有代码的情况下进 ...

  2. 从零学习Fluter(五):Flutter中手势滑动拖动已经网络请求

    从六号开始搞Flutter,到今天写这篇blog已经过了4天时间,文档初步浏览了一遍,写下了这个demo.demo源码分享在github上,现在对flutter有种说不出的喜欢了.大家一起搞吧! 废话 ...

  3. Android音乐播放器的设计与实现

    目录 应用开发技术及开发平台介绍 应用需求分析 应用功能设计及其描述 应用UI展示 一.应用开发技术及平台介绍 ①开发技术: 本系统是采用面向对象的软件开发方法,基于Android studio开发平 ...

  4. Actor模型浅析 一致性和隔离性

    一.Actor模型介绍 在单核 CPU 发展已经达到一个瓶颈的今天,要增加硬件的速度更多的是增加 CPU 核的数目.而针对这种情况,要使我们的程序运行效率提高,那么也应该从并发方面入手.传统的多线程方 ...

  5. c/c++ 继承与多态 继承时如何改变个别成员的访问属性

    问题1:若类B以private的方式继承类A,但还想让类A的某些个别成员,保持public或者protected的访问属性,这时应该怎么办? 使用using,去改变访问属性. #include < ...

  6. c/c++ 网络编程 UDP 发送端 bind 作用

    网络编程 UDP 发送端 bind 作用 upd 发送端 调用bind函数的效果:把socket特定到一个指定的端口,如果不调用bind,内核会随机分配一个端口. upd 发送端 调用bind函数的目 ...

  7. Altium Designer 复制和粘贴功能

    在使用Altium Deigner时,很多时候会使用到复制和粘贴功能,Altium Designer复制分为三步:第一步选中要复制的内容(包括点选和框选),第二步,启动COPY命令,这时光标会变成十字 ...

  8. 三数之和的golang实现

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可以包含重复的三元组. ...

  9. DecimalFormat格式化十进制数字

    DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字.该类设计有各种功能,使其能够分析和格式化任意语言环境中的数,包括对西方语言.阿拉伯语和印度语数字的支持. ...

  10. gradlew和gradle的区别

    概念理解 gradlew就是对gradle的包装和配置,gradlew是gradle Wrapper,Wrapper的意思就是包装. 因为不是每个人的电脑中都安装了gradle,也不一定安装的版本是要 ...