min_max容斥学习笔记

作用:

把一个集合的最小值转换成任意k大值,或者把最大值转换成任意k小值。

基本形式:

\[\min(\mathbb S) = \sum \limits_{\emptyset\neq \mathbb T \subseteq \mathbb S} (-1)^{|\mathbb T|+1} \max(\mathbb T),\ \ \ \ \max(\mathbb S) = \sum \limits_{\emptyset\neq \mathbb T \subseteq \mathbb S} (-1)^{|\mathbb T|+1} \min(\mathbb T) \]

证明:(以第一个式子为例)

\(\min(\mathbb S) = \sum \limits_{\emptyset\neq \mathbb T \subseteq \mathbb S} f(|\mathbb T|) \max(\mathbb T),\)

则我们思考第\(k+1\)小的数在右式中的贡献,只有在集合\(\mathbb T\)中的数全部为比第\(k+1\)小的数还小的时候才会有,由此我们可以得到贡献为\(\sum \limits_{i=0}^{k} C_k^i \cdot f(i+1)\)

我们令\(F(x) = f(x+1), G(x) = [x==0]\),则\(G(k) = \sum \limits_{i=0}^{k} C_k^i \cdot F(i)\)

对该式子进行二项式反演,则\(F(k) = \sum\limits_{i=0}^k (-1)^{k-i}C_k^i G(i)\)

所以\(f(x) = (-1)^{(k-i)} = (-1)^{(|\mathbb T+1|)}\)

同理,反之亦然。

扩展

min_max容斥更普遍的情况:

\[kth\min(\mathbb S) = \sum \limits_{\emptyset\neq \mathbb T \subseteq \mathbb S} (-1)^{|\mathbb T|-k}C_{|\mathbb T|-1}^{k-1} kth\max(\mathbb T),\ \ \ kth\max(\mathbb S) = \sum \limits_{\emptyset\neq \mathbb T \subseteq \mathbb S} (-1)^{|\mathbb T|-k}C_{|\mathbb T|-1}^{k-1} kth\min(\mathbb T) \]

证明方法和上面的方法相似,都是用二项式反演倒腾一下。

此外,这个式子在期望意义下也成立(然而不会证),即:

\[E(kth\min(\mathbb S)) = \sum \limits_{\emptyset\neq \mathbb T \subseteq \mathbb S} (-1)^{|\mathbb T|-k}C_{|\mathbb T|-1}^{k-1} E(\max(\mathbb T)) \]

例题 (HDU 4336)Card Collector

十分的裸,可以直接带到上面的式子。\(n\)也足够小

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
namespace ztd{
    using namespace std;
    typedef long long ll;
    template<typename T> inline T read(T& t) {//fast read
        t=0;short f=1;char ch=getchar();double d = 0.1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
        while (ch>='0'&&ch<='9') t=t*10+ch-'0',ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t*=f; return t;
    }
}
using namespace ztd;
const double eps = 1e-6;
const int maxn = 22;
int n, m;
double a[maxn], ans;
void dfs(int x, double p, double sign){
	if(x == n+1){
		if(p > eps) ans += (double)(sign/p);
		return;
	}
	dfs(x+1, p, sign); dfs(x+1, p+a[x], -sign);
}
inline void Main(){
	ans = 0;
    for(int i = 1; i <= n; ++i) read(a[i]);
    dfs(1, 0, -1);
    printf("%.6f\n", ans);
} 
signed main(){
    while(scanf("%d", &n) != EOF) Main();
    return 0;
}

posted @ 2020-10-12 21:38  zimindaada  阅读(168)  评论(0)    收藏  举报