【Learning】min-max容斥以及推广kth min-max容斥

min-max容斥

给定集合 S S S,设 m a x ( S ) max(S) max(S) S S S中的最大值, m i n ( S ) min(S) min(S) S S S中的最小值,则我们有一个式子:
m a x ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 m i n ( T ) max(S)=\sum_{T\subseteq S}(-1)^{|T|-1}min(T) max(S)=TS(1)T1min(T)
这个东西就叫做min-max容斥。
怎么证明?
我们考虑构造一个容斥系数 f ( x ) f(x) f(x),使得
m a x ( S ) = ∑ T ⊆ S f ( ∣ T ∣ ) m i n ( T ) max(S)=\sum_{T\subseteq S}f(|T|)min(T) max(S)=TSf(T)min(T)
考虑第 x + 1 x+1 x+1大的元素会被统计到的贡献。
则这个贡献为 ∑ i = 0 x C x i × f ( i + 1 ) \sum_{i=0}^{x}C_{x}^{i}\times f(i+1) i=0xCxi×f(i+1)
简单来说,就是枚举有哪些集合的最小值会为第 x + 1 x+1 x+1大的元素。

[ x = = 0 ] = ∑ i = 0 x C x i × f ( i + 1 ) [x==0]=\sum_{i=0}^{x}C_{x}^{i}\times f(i+1) [x==0]=i=0xCxi×f(i+1)
二项式反演一下
f ( x + 1 ) = ∑ i = 0 x ( − 1 ) x − i C x i [ i = = 0 ] f(x+1)=\sum_{i=0}^{x}(-1)^{x-i}C_{x}^{i}[i==0] f(x+1)=i=0x(1)xiCxi[i==0]
得到
f ( x + 1 ) = ( − 1 ) x f(x+1)=(-1)^x f(x+1)=(1)x
因此
f ( x ) = ( − 1 ) x − 1 f(x)=(-1)^{x-1} f(x)=(1)x1
综上, m a x ( S ) = ∑ T ⊆ S f ( ∣ T ∣ ) m i n ( T ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 m i n ( T ) max(S)=\sum_{T\subseteq S}f(|T|)min(T)\\=\sum_{T\subseteq S}(-1)^{|T|-1}min(T) max(S)=TSf(T)min(T)=TS(1)T1min(T)

证明完毕。
下面我们来看几道例题。
由于min-max容斥对于期望同样满足,所以可以很方便地解决一些概率和期望问题。

hdu4336 Card Collector

有n种卡片,每一秒都有 P i P_i Pi的概率获得一张第 i i i种卡片,求每张卡片都至少有一张的期望时间。
我们记 m a x ( S ) max(S) max(S) S S S中最后获得的那种卡片第一次获得的期望时间, m i n ( S ) min(S) min(S) S S S中的第一个获得的那种卡片第一次获得的期望时间。
仍然满足
m a x ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 m i n ( T ) max(S)=\sum_{T\subseteq S}(-1)^{|T|-1}min(T) max(S)=TS(1)T1min(T)

m i n ( T ) = 1 ∑ i ∈ T P i min(T)=\frac{1}{\sum_{i\in T} P_i} min(T)=iTPi1
直接计算即可。
代码

#include
#include
#include
#include
using namespace std;
const int N=25;
const double eps=1e-7;
double a[N],ans;
int n;
void dfs(int now,int tot,double sum){
	if(now>n){
		if(sum>eps){
			if(tot&1){
				ans+=1/sum;
			}else{
				ans-=1/sum;
			}
		}
		return;
	}
	dfs(now+1,tot,sum);
	dfs(now+1,tot+1,sum+a[now]);
}
int main(){
	while(~scanf("%d",&n)){
		for(int i=1;i<=n;i++){
			scanf("%lf",&a[i]);
		}
		ans=0;
		dfs(1,0,0);
		printf("%lf\n",ans);
	}
	return 0;
}

bzoj4036 按位或

我们记 m a x ( S ) max(S) max(S) S S S中最后被或到的元素第一次被或到的期望时间, m i n ( S ) min(S) min(S) S S S中的第一个被或到的元素第一次被或到的期望时间。
m a x ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 m i n ( T ) max(S)=\sum_{T\subseteq S}(-1)^{|T|-1}min(T) max(S)=TS(1)T1min(T)

m i n ( S ) = 1 ∑ T ∩ S ≠ ∅ P T min(S)=\frac{1}{\sum_{T\cap S≠\emptyset}P_T} min(S)=TS̸=PT1
我们如果求出了 m i n ( s ) min(s) min(s),就直接计算就好了。
怎么求?
∑ T ∩ S ≠ ∅ P T = s u m − ∑ T ∩ S = ∅ P T = s u m − ∑ T ∩ ( a l l − S ) = ( a l l − S ) P T \sum_{T\cap S≠\emptyset}P_T\\=sum-\sum_{T\cap S=\emptyset}P_T\\=sum-\sum_{T\cap (all-S)=(all-S)}P_T TS̸=PT=sumTS=PT=sumT(allS)=(allS)PT
然后 F W T FWT FWT计算即可。
代码

#include
#include
#include
#include
using namespace std;
const int N=1050005;
int n,all,tmp,cnt[N];
double sum,res,ans,a[N];
int main(){
	scanf("%d",&n);
	all=1<<n;
	for(int i=0;i<all;i++){
		cnt[i]=cnt[i>>1]+(i&1);
		scanf("%lf",&a[i]);
		sum+=a[i];
		if(fabs(a[i])>1e-6){
			tmp|=i;
		}
	}
	if(tmp!=all-1){
		puts("INF");
		return 0;
	}
	for(int i=1;i<all;i<<=1){
		for(register int j=0;j<all;j+=(i<<1)){
			for(register int k=j;k<j+i;k++){
				a[k+i]+=a[k];
			}
		}
	}
	for(register int i=0;i<all;i++){
		res=sum-a[all-1-i];
		if(res>1e-6){
			res=1/res;
		}else{
			res=0;
		}
		if(cnt[i]&1){
			ans+=res;
		}else{
			ans-=res;
		}
	}
	printf("%.8lf\n",ans);
	return 0;
}

推广 kth min-max容斥

我们考虑构造一个容斥系数 f ( x ) f(x) f(x),使得
k t h m a x ( S ) = ∑ T ⊆ S f ( ∣ T ∣ ) m i n ( T ) kthmax(S)=\sum_{T\subseteq S}f(|T|)min(T) kthmax(S)=TSf(T)min(T)
考虑第 x + 1 x+1 x+1大的元素会被统计到的贡献。
这个贡献为 ∑ i = 0 x C x i × f ( i + 1 ) \sum_{i=0}^{x}C_{x}^{i}\times f(i+1) i=0xCxi×f(i+1)

[ x = = k − 1 ] = ∑ i = 0 x C x i × f ( i + 1 ) [x==k-1]=\sum_{i=0}^{x}C_{x}^{i}\times f(i+1) [x==k1]=i=0xCxi×f(i+1)
二项式反演一下
f ( x + 1 ) = ∑ i = 0 x ( − 1 ) x − i C x i [ i = = k − 1 ] f(x+1)=\sum_{i=0}^{x}(-1)^{x-i}C_{x}^{i}[i==k-1] f(x+1)=i=0x(1)xiCxi[i==k1]
得到
f ( x + 1 ) = ( − 1 ) ( x − ( k − 1 ) ) C x k − 1 f(x+1)=(-1)^{(x-(k-1))}C_{x}^{k-1} f(x+1)=(1)(x(k1))Cxk1
因此
f ( x ) = ( − 1 ) x − k C x − 1 k − 1 f(x)=(-1)^{x-k}C_{x-1}^{k-1} f(x)=(1)xkCx1k1
综上, k t h m a x ( S ) = ∑ T ⊆ S f ( ∣ T ∣ ) m i n ( T ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − k C ∣ T ∣ − 1 k − 1 m i n ( T ) kthmax(S)=\sum_{T\subseteq S}f(|T|)min(T)\\=\sum_{T\subseteq S}(-1)^{|T|-k}C_{|T|-1}^{k-1}min(T) kthmax(S)=TSf(T)min(T)=TS(1)TkCT1k1min(T)
有一道例题,还没弄懂,不会做:重返现世
这道题是ypl学长的神题QwQ
upd:写了这一题,贴一个题解吧
【Learning】min-max容斥以及推广kth min-max容斥_第1张图片
边界需要仔细考虑,非常巧妙地设成-1。(额怎么说呢初始化为-1手算一下发现是对的)

#include
#include
#include
using namespace std;
const int N=1005,M=10005,K=15,mod=998244353;
int n,m,k,ans,p[N],f[2][M][K];
int fastpow(int a,int x){
    int res=1;
    while(x){
        if(x&1){
            res=1LL*res*a%mod;
        }
        x>>=1;
        a=1LL*a*a%mod;
    }
    return res;
}
int main(){
    scanf("%d%d%d",&n,&k,&m);
    k=n-k+1;
    for(int i=1;i<=n;i++){
        scanf("%d",&p[i]);
    }
    for(int i=1;i<=k;i++){
        f[0][0][i]=-1;
    }
    int sta=1;
    for(int i=1;i<=n;i++,sta^=1){
        for(int j=0;j<=m;j++){
            for(int x=1;x<=k;x++){
                f[sta][j][x]=f[sta^1][j][x];
                if(j-p[i]>=0){
                    f[sta][j][x]=(1LL*f[sta][j][x]+f[sta^1][j-p[i]][x-1]-f[sta^1][j-p[i]][x])%mod;
                }
            }
        }
    }
    for(int i=1;i<m;i++){
        ans=(ans+1LL*fastpow(i,mod-2)*m%mod*f[sta^1][i][k]%mod)%mod;
    }
    ans=(ans+f[sta^1][m][k])%mod;
    printf("%d\n",(ans+mod)%mod);
    return 0;
}

你可能感兴趣的:(min-max容斥,容斥原理,FWT)