题目描述
分析
解法一:正难则反,对症下药
正难则反
存在一个大于等于4的连通块的概率,等价于:1减去所有连通块都小于等于3的概率。
状态函数:
f(i,j,k)表示i个点的图中,有大小为2的连通块j个,大小为3的连通块k个,大小为1的联通块有i-2*j-3*k个的概率。
边界:
f(0,0,0)=1;
转移方程:
刷表法——分析在f(i,j,k)的基础上新加入1个点的状态转移:
情况1、若新点与前面i个点中任何一个都不连边,则状态转移成 f(i+1,j,k)+=f(i,j,k)*(1-p)^i
情况2、若新点与前面i个点中任意一个大小为1的连通块 连一条边构成一个大小为2的连同块,则状态转移成:f(i+1,j+1,k)+=f(i,j,k)*(i-2*j-3*k)*p*(1-p)^(i-1)
情况3、若新点与前面i个点中任意 两个 大小为1的连通块连一条边构成一个大小为3的连同块,则状态转移成:f(i+1,j,k+1)+=f(i,j,k)*C(i-2*j-3*k,2)*p*p*(1-p)^(i-2)
情况4、若新点与前面i个点中任意一个大小为2的连通块连一条边构成一个大小为3的连同块,则状态转移成:f(i+1,j-1,k+1)+=f(i,j,k)*2*j*p*(1-p)^(i-1)
情况5、若新点与前面i个点中任意一个大小为2的连通块连两条边构成一个大小为3的连同块(大小为2的连通块的两个点分别连一条边),则状态转移成:f(i+1,j-1,k+1)+=f(i,j,k)*j*p*(1-p)^(i-1)
对不起我抄题解了
解法二:正难则反+大佬就是不一样——%%%通解通法
借鉴了某位超级大犇的算法,强的一p。
f(i)表示i个结点能够连通的概率
g(i)表示i个结点无法满足至少有一个连通块大于等于4的概率
正难则反:先考虑加入结点不能使图连通。对于新加入的结点,分析:
若它和已有的i-1个结点形成了一块大小为j的连通块,相当于从i-1个结点中选择j-1个出来和现在的这个结点形成连通块,那么形成的概率为f(j)*C(i-1,j-1)
要使这个连通块不连通,则剩下的i-j个结点和这个连通块里面的所有节点都不能连通,概率为((1-p)^(i-j))^j=(1-p)^(j*(i-j))
所以说此时形成大小为j的连通块并且这i个结点不连通的概率:f(j)*C(i-1,j-1)*(1-p)^(j*(i-j))
g(i)类似,在这j个结点不与外界连通的同时,剩下的i-j个结点的大小也不能大于等于4,所以概率为:f(j)*C(i-1,j-1)*(1-p)^(j*(i-j))*g(i-j)
综上,f(i)=1-sum{f(j)*C(i-1,j-1)*(1-p)^(j*(i-j)) | 1<=j<i}
g(i)=sum{f(j)*C(i-1,j-1)*(1-p)^(j*(i-j))*g(i-j) | 1<=j<=3}
当然,i<=3时,就算这i个结点全部连通,都无法满足至少有一个连通块大于等于4,所以说此时g(i)+=f(i)
但是当j*(i-j)很大的时候,(1-p)^(j*(i-j))会趋近于0,题目精度要求不高,取对数避免
最终答案ans=1-g(n)
贴一下算法二的代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int maxn=105,maxm=10005;
int n;
double p;
void workin(){
scanf("%d%lf",&n,&p);
p/=1000;
return;
}
double C[maxn][maxn],fact[maxn],epow[maxm];
void ready(){
epow[0]=0;
for(int i=1;i<=10000;i++){
epow[i]=epow[i-1]+log(1-p);
}
fact[0]=fact[1]=0;
for(int i=2;i<=n;i++){
fact[i]=fact[i-1]+log(i);
}
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
C[i][j]=fact[i]-fact[j]-fact[i-j];
}
}
return;
}
double d[maxn],f[maxn];
void dp(){
d[1]=f[1]=0;
for(int i=2;i<=n;i++){
double sum=0;
for(int j=1;j<i;j++){
sum+=exp(d[j]+C[i-1][j-1]+epow[j*(i-j)]);
}
d[i]=log(1-sum);
sum=0;
for(int j=1;j<i && j<=3;j++){
sum+=exp(d[j]+C[i-1][j-1]+epow[j*(i-j)]+f[i-j]);
}
f[i]=log(sum);
if(i<4){
f[i]=log(exp(f[i])+exp(d[i]));
}
}
return;
}
void solve(){
ready();
dp();
printf("%.4lf",1-exp(f[n]));
return;
}
int main(){
freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
workin();
solve();
return 0;
}
最后说一句,fuse123fuseTQL!!!!