版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/84373478
一个叫做
min−max容斥的东西
大概就是:
max(a,b)=a+b−min(a,b)
max(a,b,c)=a+b+c−min(a,b)−min(b,c)−min(a,c)+min(a,b,c)
当求一个集合的
max值很复杂的时候,就可以用
MMI来转化成求
min,通常在计数
dp或者概率期望中用到。
有两道例题:
昨天写的luogu3175
其实就用到了这个思想
样例就是这样算的:
设
t[i]表示第
i位变成
1的时间
E[max(t[0],t[1])]=E[t[0]]+E[t[1]]−E[min(t[0],t[1])]
E[t[0]]=1/Pr[在一次操作中,第0位变成1的概率]=1/(1/2)=2
E[t[1]]=1/Pr[在一次操作中,第1位变成1的概率]=1/(1/2)=2
E[min(t[0],t[1])]=1/Pr[在一次操作中,第0位或第1位变成1的概率]=1/(3/4)=4/3
2+2−4/3=8/3=2.66666667
E[min(t[a],t[b],t[c]...)]=
1/Pr[在一次操作中,第a位或第b位或第c位或...变成1的概率]=
1/(1−Pr[在一次操作中,第a,b,c,...均没有变成1的概率])=
第a,b,c,...均没有变成1等价于操作的集合是(全集−a,b,c,...)的一个子集
求出(全集−a,b,c,...)的所有子集的概率之和
然后就是用
FMT优化
dp的过程了
第二道是hdu4624
是clj出的题!
同样也是用
MMI的想法,把
E[max{S}]转化成
∑T∈S(−1)∣T∣+1×E[min{T}]
就可以概率
dp了,注意到:
最终有用的
- 集合大小奇偶性,决定正负
- 有多少个操作,可以操作到集合中的某一个点 / 有多少个操作,操作不到集合中的任意一个点
所以设
f[i][j][k]表示
集合中选择了
i染成黑色,集合大小奇偶性是
j(0,1) 有
k个操作,操作不到任意一个点
f[i][j][k]是集合选取的方案数。
有转移方程:
f[i][j][k]−>f[l][j xor 1][k+(l−i)×(l−i−1)/2]
第三维是因为
l,r同时选到
i到
l中间的任意一点都是操作不到任意一个点的
计算最终的期望结果的时候就是用方案数乘概率,因为枚举
i的子集时发现只有
pi变,是一个等比数列,最终贡献就是
(1−p)1
因此只需大概
O(504)预处理
f和最终期望结果,询问时
O(1)回答
但是题目要求保留
15位小数,貌似要用浮点高精度,好麻烦的样子就找标程拍了拍
qwq
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 55
#define LL long long
using namespace std;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int t,n;
LL f[maxn][2][5005];
double E[maxn];
inline void prework(){
f[0][0][0]=1;
for(int i=0;i<=50;i++)
for(int k=0;k<=(i+1)*i/2;k++)
for(int j=0;j<=1;j++)
if(f[i][j][k]){
for(int l=i+1;l<=50;l++)
f[l][j^1][k+(l-i)*(l-i-1)/2]+=f[i][j][k];
}
for(int i=1;i<=50;i++)
for(int j=0;j<=i;j++)
for(int k=0;k<=j*(j+1)/2;k++){
long double p=0;
if(f[j][0][k]){
p=1.0*(k+(i-j+1)*(i-j)/2)/(1.0*(i+1)*i/2);
if(p==1.0) continue;
E[i]-=1.0*f[j][0][k]/(1.0-p);
}
if(f[j][1][k]){
p=1.0*(k+(i-j+1)*(i-j)/2)/(1.0*(i+1)*i/2);
if(p==1.0) continue;
E[i]+=1.0*f[j][1][k]/(1.0-p);
}
}
}
int main(){
prework();
t=rd();
while(t--){
n=rd();
printf("%.5lf\n",E[n]);
}
return 0;
}