min_max容斥学习笔记

作用:

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

基本形式:

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

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

(min(mathbb S) = sum limits_{emptyset eq 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) = sumlimits_{i=0}^k (-1)^{k-i}C_k^i G(i))

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

同理,反之亦然。

扩展

min_max容斥更普遍的情况:

[kthmin(mathbb S) = sum limits_{emptyset eq mathbb T subseteq mathbb S} (-1)^{|mathbb T|-k}C_{|mathbb T|-1}^{k-1} kthmax(mathbb T), kthmax(mathbb S) = sum limits_{emptyset eq mathbb T subseteq mathbb S} (-1)^{|mathbb T|-k}C_{|mathbb T|-1}^{k-1} kthmin(mathbb T) ]

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

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

[E(kthmin(mathbb S)) = sum limits_{emptyset eq 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
", ans);
} 
signed main(){
    while(scanf("%d", &n) != EOF) Main();
    return 0;
}

原文地址:https://www.cnblogs.com/zimindaada/p/13805704.html