min-max容斥

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)T+1min(T)

E ( m a x ( S ) ) = ​ ∑ T ⊆ S ( − 1 ) ∣ T ∣ + 1 E ( m i n ( T ) ) E(max(S))=​\sum_{T\subseteq S}(−1)^{∣T∣+1}E(min(T)) E(max(S))=TS(1)T+1E(min(T))

证明:
我们考虑集合中的每个数对答案的贡献(不可重集),然后显然
应用:
[HAOI2015]按位或

解析

  • 转化,有n维空间,每维0~1,每维有一个由0->1的时间,求所有维度时间max的期望
  • 直接min-max容斥,考虑集合,对于某个子集考虑求出其某位最早出现1的时间的期望
  • 直接推数学公式发现是个等差比数列,最后发现就是要求每个集合其子集的概率之和
  • 直接高维前缀和
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-3;
const int N=20;
double a[1<<N],ans;
int n;
int get_sum(int x){
	int ans=0;
	while (x) ans+=(x&1),x>>=1;
	return (ans+1)&1;
}
int main(){
	scanf("%d",&n);
	for (int i=0;i<(1<<n);i++) scanf("%lf",&a[i]);
	for (int i=0;i<n;i++)
	 for (int j=0;j<(1<<n);j++) if ((1<<i)&j) a[j]+=a[j^(1<<i)];
	int len=(1<<n)-1; 
	for (int i=1;i<(1<<n);i++) 
		if (fabs(1.0-a[i^len])>eps) if (get_sum(i)) ans=(ans-1.0/(1.0-a[i^len])); else ans+=1.0/(1.0-a[i^len]);
		else {puts("INF"); return 0;}
	printf("%.10lf\n",ans);
}

51nod1355 斐波那契的最小公倍数

解析

在斐波那契数列中有个结论
g c d ( F a , F b ) = F g c d ( a , b ) gcd(F_{a},F_{b})=F_{gcd(a,b)} gcd(Fa,Fb)=Fgcd(a,b)
证明略
但是本题让我们求lcm
这就启发我们思考lcm与gcd直接的关系
但是直接想好像没有什么发现
我们把每个数抽象成一个由质数为基础的向量空间
我们发现对于lcm就是每个质数维度求max,对于gcd就是每个质数维度求min
我们考虑min-max容斥
l c m ( F { S } ) = ∏ T ⊆ S g c d ( F { T } ) ( − 1 ) ∣ T ∣ + 1 = ∏ T ⊆ S F g c d { T } ( − 1 ) ∣ T ∣ + 1 lcm(F_{\left\{S\right\}})=\prod_{T\subseteq S}gcd(F_{\left\{T\right\}})^{(-1)^{|T|+1}}=\prod_{T\subseteq S}{F_{gcd\left\{T\right\}}}^{(-1)^{|T|+1}} lcm(F{S})=TSgcd(F{T})(1)T+1=TSFgcd{T}(1)T+1
利用上述知识似乎到这一步就止了,我们需要更为强力的武器
g c d { T } ? gcd\left\{T\right\}? gcd{T}?莫比乌斯反演?
给个不那么严谨的做法,我们定义 g i g_{i} gi为gcd为i及其倍数的质数所产生的贡献,然后直接容斥

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10;
const int mod=1e9+7;
int a[N],g[N],f[N];
int n,x;
ll ksm(ll x,ll y){
   ll ans=1;
   for (;y;y>>=1,x=(x*x)%mod) if (y&1) ans=(ans*x)%mod;
   return ans;
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&x),a[x]++;
	f[0]=0; f[1]=1;
	for (int i=2;i<N;i++) f[i]=(f[i-1]+f[i-2])%mod;
	for (int i=1;i<N;i++) {
		int sum=0;
		for (int j=1;j*i<N;j++) sum+=a[j*i];
		if (sum) g[i]=1;
	}
	for (int i=N-1;i>=1;i--)
	 for (int j=2;j*i<N;j++) g[i]-=g[i*j];
	ll ans=1; 
	for (int i=1;i<N;i++) if (g[i]!=0) {
	if (g[i]>0) ans=ans*ksm(f[i],g[i])%mod;
	else ans=ans*ksm(ksm(f[i],-g[i]),mod-2)%mod;
	}
	printf("%lld\n",ans);
}  

拓展K-th Min-Max

我们考虑 k − t h M a x ( S ) = ∑ T ⊆ S f ( ∣ T ∣ ) m i n ( T ) k-thMax(S)=\sum_{T\subseteq S}f(|T|)min(T) kthMax(S)=TSf(T)min(T)
我们每个数被算到的贡献,不妨假设为第x大的数,总共有n个元素
其贡献为 ∑ i = 0 x − 1 C ( x − 1 , i ) f ( i + 1 ) \sum_{i=0}^{x-1}C(x-1,i)f(i+1) i=0x1C(x1,i)f(i+1),根据要求 [ x = = k ] = ∑ i = 0 x − 1 C ( x − 1 , i ) f ( i + 1 ) [x==k]=\sum_{i=0}^{x-1}C(x-1,i)f(i+1) [x==k]=i=0x1C(x1,i)f(i+1)
直接上二项式反演
f ( i ) = C ( i − 1 , k − 1 ) ( − 1 ) i − k f(i)=C(i-1,k-1)(-1)^{i-k} f(i)=C(i1,k1)(1)ik
k − t h M a x ( S ) = ∑ T ⊆ S C ( ∣ T ∣ − 1 , k − 1 ) ( − 1 ) ∣ T ∣ − k m i n ( T ) k-thMax(S)=\sum_{T\subseteq S}C(|T|-1,k-1)(-1)^{|T|-k}min(T) kthMax(S)=TSC(T1,k1)(1)Tkmin(T)
对于期望依然成立

重返现世

解析

  • 我们考虑每种物品都有一个出现的时间,求所有物品出现时间第k小的期望
  • 首先转化为求第n-k+1大的期望
  • min的期望非常好求,对于某个子集T, E m i n ( T ) = m ∑ x ⊆ T p x Emin(T)=\frac{m}{\sum_{x\subseteq T}p_{x}} Emin(T)=xTpxm
  • 考虑min-max容斥,注意到k十分小,启发我们思考维护 C ( ∣ T ∣ − 1 , k − 1 ) ( − 1 ) ∣ T ∣ − k C(|T|-1,k-1)(-1)^{|T|-k} C(T1,k1)(1)Tk的贡献
  • 考虑dp[i][j]表示当 k = i , ∑ p = j k=i,\sum p=j k=i,p=j时产生的贡献,那么新加入位置x, d p ′ [ i ] [ j ] = d p [ i − 1 ] [ j − p x ] − d p [ i ] [ j − p x ] dp'[i][j]=dp[i-1][j-p_{x}]-dp[i][j-p_{x}] dp[i][j]=dp[i1][jpx]dp[i][jpx](利用组合数的递推式)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
const int D=13;
const int N=1010;
const int M=10010;
int dp[D][M],p[N],inv[M];
int n,k,m;
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int mul(int x,int y){return (ll)x*y%mod;}
int ksm(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=mul(x,x)) if (y&1) ans=mul(ans,x);
	return ans;
}
void init(){
	inv[0]=inv[1]=1; 
	for (int i=2;i<M;i++) inv[i]=mul(mod-mod/i,inv[mod%i]);
}
int main(){
	scanf("%d%d%d",&n,&k,&m);
	k=n-k+1;
	for (int i=1;i<=n;i++) scanf("%d",&p[i]);
	init();
	dp[0][0]=1;
	for (int i=1;i<=n;i++) {
		for (int j=k;j>=1;j--) 
			for (int q=m;q>=p[i];q--) 
			 dp[j][q]=add(dp[j][q],dec(dp[j-1][q-p[i]],dp[j][q-p[i]]));
	}
	int ans=0;
	for (int i=0;i<=m;i++) ans=add(ans,mul(inv[i],dp[k][i]));
	printf("%d\n",mul(ans,m));
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值