• 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;
    }
    
    
  • 相关阅读:
    Linux学习 :字符设备框架
    Linux学习 :Uboot, Kernel, 根文件系统初步分析
    Linux学习 : 裸板调试 之 配置UART
    Linux学习 : 裸板调试 之 配置使用NAND FLASH
    Linux学习 : 裸板调试 之 使用MMU
    Linux I2C总线控制器驱动(S3C2440)
    Linux I2C总线设备驱动模型分析(ov7740)
    Linux摄像头驱动学习之:(六)UVC-基本框架代码分析
    【Java】 剑指offer(25) 合并两个排序的链表
    【Java】 剑指offer(24) 反转链表
  • 原文地址:https://www.cnblogs.com/zimindaada/p/13805704.html
Copyright © 2020-2023  润新知