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)=∑T⊆S(−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))=∑T⊆S(−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);
}
解析
在斐波那契数列中有个结论
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})=∏T⊆Sgcd(F{T})(−1)∣T∣+1=∏T⊆SFgcd{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)
k−thMax(S)=∑T⊆Sf(∣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=0x−1C(x−1,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=0x−1C(x−1,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(i−1,k−1)(−1)i−k
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)
k−thMax(S)=∑T⊆SC(∣T∣−1,k−1)(−1)∣T∣−kmin(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)=∑x⊆Tpxm
- 考虑min-max容斥,注意到k十分小,启发我们思考维护 C ( ∣ T ∣ − 1 , k − 1 ) ( − 1 ) ∣ T ∣ − k C(|T|-1,k-1)(-1)^{|T|-k} C(∣T∣−1,k−1)(−1)∣T∣−k的贡献
- 考虑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[i−1][j−px]−dp[i][j−px](利用组合数的递推式)
#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));
}