min-max 容斥学习笔记

关于 m i n − m a x min-max minmax容斥,我们希望构造一个系数使得
m a x ( s ) = ∑ T ⊆ S f ∣ T ∣ m i n ( T ) max(s)=\sum_{T\subseteq S}f_{|T|}min(T) max(s)=TSfTmin(T)
考虑 k + 1 k+1 k+1 大的贡献
m a x ( s ) = ∑ i = 0 k ( k i ) f i + 1 = [ k = 0 ] max(s)=\sum_{i=0}^k\binom{k}{i}f_{i+1}=[k=0] max(s)=i=0k(ik)fi+1=[k=0]
反演得 f k + 1 = ∑ i = 0 k ( − 1 ) k − i [ i = 0 ] ( k i ) = ( − 1 ) k f_{k+1}=\sum_{i=0}^k(-1)^{k-i}[i=0]\binom{k}{i}=(-1)^k fk+1=i=0k(1)ki[i=0](ik)=(1)k
所以 f ∣ T ∣ = ( − 1 ) ∣ T ∣ − 1 f_{|T|}=(-1)^{|T|-1} fT=(1)T1

关于 k t h − m i n − m a x kth-min-max kthminmax 容斥,同上考虑 k + 1 k + 1 k+1 大的贡献
k t h − m a x ( s ) = ∑ i = 0 k ( k i ) f i + 1 = [ k = r k − 1 ] kth-max(s)=\sum_{i=0}^k\binom{k}{i}f_{i+1}=[k=rk-1] kthmax(s)=i=0k(ik)fi+1=[k=rk1]
反演得 f k + 1 = ∑ i = 0 k ( − 1 ) k − i ( k i ) [ i = r k − 1 ] = ( − 1 ) k − r k + 1 ( k r k − 1 ) f_{k+1}=\sum_{i=0}^k(-1)^{k-i}\binom{k}{i}[i=rk-1]=(-1)^{k-rk+1}\binom{k}{rk-1} fk+1=i=0k(1)ki(ik)[i=rk1]=(1)krk+1(rk1k)
所以 f ∣ T ∣ = ( − 1 ) ∣ T ∣ − r k ( ∣ T ∣ − 1 r k − 1 ) f_{|T|}=(-1)^{|T|-rk}\binom{|T|-1}{rk-1} fT=(1)Trk(rk1T1)


HDU4624:Endless Spin
题意:给一个长为m 的白球序列,每次随机一个区间,把区间内的球全部染为黑色,求把所有球染黑的期望步数
考虑到 a n s = m a x ( p i ) ans=max(p_i) ans=max(pi)不好求,转换为求 m i n ( p i ) min(p_i) min(pi) 然后反演
一个集合的最小期望就是这个集合第一个被染到的点的期望时间
发现为 总个数/能覆盖一个点的区间个数
但我们不能枚举集合,考虑到能覆盖到一个点的区间个数很少,想到了 d p dp dp
f i , j , 0 / 1 f_{i,j,0/1} fi,j,0/1 为当前选到了第 i 个位置,不能覆盖一个点的个数是 j,最后一维是容斥的系数
f i , j + ( i − p − 1 2 ) + = f p , j f_{i,j+\binom{i-p-1}{2}}+=f_{p,j} fi,j+(2ip1)+=fp,j

#include<bits/stdc++.h>
#define N 55
typedef long long ll;
using namespace std;
int T, n; ll f[N][N*N][2];
int P(int x){ return x * (x + 1) / 2;}
int main(){
	scanf("%d", &T);
	while(T--){
		scanf("%d", &n);
		memset(f, 0, sizeof(f));
		f[0][0][0] = 1;
		for(int i = 1; i <= n; i++)
			for(int j = 0; j < i; j++)
				for(int k = P(i - j - 1); k <= P(i); k++)
					for(int l = 0; l < 2; l++)
						f[i][k][l] += f[j][k - P(i - j - 1)][l ^ 1];
		double ans = 0; int tot = P(n);
		for(int i = 1; i <= n; i++){
			for(int j = 0; j <= P(i); j++){
				for(int l = 0; l < 2; l++){
					if(l & 1) {if(f[i][j][l]) ans += 1.0 * f[i][j][l] * tot / (tot - j - P(n - i));} 
					else { if(f[i][j][l])ans -= 1.0 * f[i][j][l] * tot / (tot - j - P(n - i));}
				}
			}
		} printf("%.15lf\n", ans);
	} return 0;
}

重返现世
k t h − m i n ( p i ) kth-min(p_i) kthmin(pi)
考虑到 n − k n-k nk很小,于是把 k k k换成 n − k + 1 n-k+1 nk+1,变成求 k t h − m a x ( p i ) kth-max(p_i) kthmax(pi)
k t h − m a x ( S ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − k ( ∣ T ∣ − 1 k − 1 ) m i n ( T ) kth-max(S)=\sum_{T\subseteq S}(-1)^{|T|-k}\binom{|T|-1}{k-1}min(T) kthmax(S)=TS(1)Tk(k1T1)min(T)
m i n ( S ) = m ∑ i ∈ S p i min(S)=\frac{m}{\sum_{i\in S}p_i} min(S)=iSpim
同样又不能枚举集合,而 ∑ p i \sum p_i pi 的个数非常少
于是令 f i , j , k f_{i,j,k} fi,j,k 表示到第 i 个,当前 p i p_i pi 的和,现在求的是第 k 大的 ∑ T ⊆ S ( − 1 ) ∣ T ∣ − k ( ∣ T ∣ − 1 k − 1 ) m i n ( T ) \sum_{T\subseteq S}(-1)^{|T|-k}\binom{|T|-1}{k-1}min(T) TS(1)Tk(k1T1)min(T)的值
如果不选 f i , j , k = f i − 1 , j , k f_{i,j,k}=f_{i-1,j,k} fi,j,k=fi1,j,k
如果选,所有的 ∣ T ∣ |T| T 要变成 ∣ T ∣ + 1 |T|+1 T+1
也就是 ∑ T ⊆ S ( − 1 ) ∣ T ∣ − k ( ∣ T ∣ − 1 k − 1 ) m i n ( T ) \sum_{T\subseteq S}(-1)^{|T|-k}\binom{|T|-1}{k-1}min(T) TS(1)Tk(k1T1)min(T)变成 ∑ T ⊆ S ( − 1 ) ∣ T + 1 ∣ − k ( ∣ T ∣ k − 1 ) m i n ( T ) \sum_{T\subseteq S}(-1)^{|T+1|-k}\binom{|T|}{k-1}min(T) TS(1)T+1k(k1T)min(T)
∑ T ⊆ S ( − 1 ) ∣ T + 1 ∣ − k ( ∣ T ∣ k − 1 ) m i n ( T ) = ∑ T ⊆ S ( − 1 ) ∣ T + 1 ∣ − k ( ( ∣ T ∣ − 1 k − 1 ) + ( ∣ T − 1 ∣ k − 2 ) ) m i n ( T ) \sum_{T\subseteq S}(-1)^{|T+1|-k}\binom{|T|}{k-1}min(T)=\sum_{T\subseteq S}(-1)^{|T+1|-k}(\binom{|T|-1}{k-1}+\binom{|T-1|}{k-2})min(T) TS(1)T+1k(k1T)min(T)=TS(1)T+1k((k1T1)+(k2T1))min(T)
所以 f i , j , k = f i − 1 , j − p i , k − 1 − f i − 1 , j − p i , k f_{i,j,k}=f_{i-1,j-p_i,k-1}-f_{i-1,j-p_i,k} fi,j,k=fi1,jpi,k1fi1,jpi,k

#include<bits/stdc++.h>
#define N 10050
using namespace std;
typedef long long ll;
const int Mod = 998244353;
ll add(ll a, ll b){ return (a + b) % Mod;}
ll mul(ll a, ll b){ return (a * b) % Mod;}
ll power(ll a, ll b){ll ans = 1; for(;b;b>>=1, a=mul(a,a)) if(b&1) ans = mul(ans,a); return ans;}
ll f[2][N][11]; int n, k, m, p[N];
int main(){
	scanf("%d%d%d", &n, &k, &m); k = n - k + 1;
	for(int i = 1; i <= n; i++) scanf("%d", &p[i]);
	int now = 0;
	f[now][0][0] = 1;
	for(int i = 1; i <= n; i++){
		now ^= 1; memset(f[now], 0, sizeof(f[now]));
		f[now][0][0] = 1;
		for(int j = 1; j <= p[i] - 1; j++) for(int l = 1; l <= k; l++) f[now][j][l] = f[now^1][j][l];
		for(int j = p[i]; j <= m; j++) for(int l = 1; l <= k; l++)
			f[now][j][l] = add(f[now^1][j][l], add(f[now^1][j-p[i]][l-1], Mod - f[now^1][j-p[i]][l]));
	}
	ll ans = 0;
	for(int i = 1; i <= m; i++) ans = add(ans, mul(f[now][i][k], mul(power(i, Mod-2), m)));
	cout << ans; return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值