BZOJ 3925: [Zjoi2015]地震后的幻想乡(概率)

时间:2023-03-08 17:19:06

CLJ就是喜欢出ctsc上讲的东西,看来还是得找时间把他的那几道题做下

首先记f(x)为答案>x的概率,那么把这个东西从0到1积分就是答案了

f(x)<=>边小于x不能使图联通的概率

这个有点难求,考虑求使图联通的概率

记f(s)为集合s联通的概率,那么f(s)=1-sigma(f(s')*(1-x)^cnt) (s'属于s且s'一定包含某点k,cnt为链接s'与Cs s'的边数)

可以发现f(s)是个多项式,就可以积分了

由于还没用上64位评测系统,double还是不能过,只好用__float128,比较慢而已

CODE:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef vector<__float128> ploy;
ploy operator + (ploy x,ploy y){
ploy ans(max(x.size(),y.size()),);
for (int i=;i<ans.size();i++) {
if (i<x.size()) ans[i]+=x[i];
if (i<y.size()) ans[i]+=y[i];
}
return ans;
}
ploy operator - (ploy x,ploy y) {
ploy ans(max(x.size(),y.size()),);
for (int i=;i<ans.size();i++) {
if (i<x.size()) ans[i]+=x[i];
if (i<y.size()) ans[i]-=y[i];
}
return ans;
}
ploy operator * (ploy x,ploy y) {
ploy ans(x.size()+y.size()-,);
for (int i=;i<x.size();i++)
for (int j=;j<y.size();j++)
ans[i+j]+=x[i]*y[j];
return ans;
}
bool b[];
ploy f[],quick[];
int n,m;
struct edges{int x,y;}e[];
ploy ONE(,);
inline void print(ploy x){
printf("%d\n",x.size());
for (int i=;i<x.size();i++) printf("%lf ",double(x[i]));
printf("\n");
}
void dp(int x){
if (b[x]) return ;
b[x]=;
ploy tmp(,);
for (int i=;i<(<<n);i++) {
if (!(i&)) continue;
if ((i&x)!=i) continue;
if (i==x) continue;
int j=x^i;
int cnt=;
for (int k=;k<=m*;k++)
if (i&(<<e[k].x)&&j&(<<e[k].y)) cnt++;
dp(i);
tmp=tmp+f[i]*quick[cnt];
}
f[x]=ONE-tmp;
}
__float128 cal(ploy x) {
__float128 ans=;
for (int i=;i<x.size();i++) ans+=x[i]*1.0/(i+);
return ans;
}
int main() {
scanf("%d%d",&n,&m);
quick[]=ONE;
quick[]=ploy(,);
quick[][]=;quick[][]=-;
for (int i=;i<=m;i++) quick[i]=quick[i-]*quick[];
for (int i=;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
x--,y--;
e[i*-]=(edges){x,y};
e[i*]=(edges){y,x};
}
b[]=;f[]=ONE;
dp((<<n)-);
ploy ans=ONE-f[(<<n)-];
printf("%.6lf\n",double(cal(ans)));
return ;
}