[cf235D]Graph Game
Description
给定一张$n$个顶点$n$条边图,定义过程$solve(G),G$是一个连通块.
$1.totalCost=totalCost+|G|,|G|$表示图$G$中的节点个数.
$2.$在图$G$中随机选择一个节点$x$(每个点被选中的概率相等).
$3.$在图$G$中删除节点$x$.
$4.$图被分裂为几个连通块.
$5.$分治处理所有的子连通块$G',solve(G')$.
求$totalCost$的数学期望.
Input
第一行一个整数$n$,表示图中点的数量和边的数量.
接下来$n$行,每行两个空格隔开的整数$a_i,b_i$,表示一条$(a_i,b_i)$的无向边.
节点从$0$开始编号.
Output
仅一行输出一个实数,表示$totalCost$的数学期望.和标准答案绝对误差或相对误差在$10^{−6}$以内都被认为正确.
Sample Input
5
3 4
2 3
2 4
0 4
1 2
Sample Output
13.1666666666
HINT
$n\;\leq\;3000$
Solution
- 树的情况
定义事件$P(A,B)$为在删除点$A$时,$B$点仍与$A$点连通的概率.
显然当一个事件发生时,答案$+1$,所以答案的期望值为所有事件的总和.
$P(A,B)=$在$AB$路径上的点数的倒数.
设$AB$路径上的点数为$n$.
- 当连通块大小$=n$时,连通块为链$AB$.只有选取$A$时才会发生事件$P(A,B)$,概率为$\frac{1}{n}$.
- 当连通块大小$\geq\;n$,为$x$时,选取的点有两种情况:
- 选取在$AB$链上,这时的概率为$\frac{n}{x}\times\frac{1}{n}$;
- 选取不在$AB$链上,概率为$\frac{x-n}{x}\times\frac{1}{n}$.
- 相加为$\frac{1}{n}$.
- 原题的情况
- $AB$间的路径没通过环,结论同树.
- $AB$间的路径经过了环,可以将路径上的点分成三部分:
- 环外的点$x$,数量记为$X$.
- 环上$A$到$B$顺时针的点$y$,数量记为$Y$.
- 环上$B$到$A$顺时针的点$z$,数量记为$Z$.
- 在选取$A$之前,$y,z$之间必须有一条不能被取,不能选取$x$.期望值为$\frac{1}{X+Y}+\frac{1}{X+Z}-\frac{1}{X+Y+Z}$.
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 3005
using namespace std;
struct graph{
int nxt,to;
}e[N<<1];
int g[N],t[N],d1[N],d2[N],n,cnt;
bool ins[N];
double ans;
queue<int> q;
inline int read(){
int ret=0;char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c)){
ret=(ret<<1)+(ret<<3)+c-'0';
c=getchar();
}
return ret;
}
inline void addedge(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline void toposort(){
int u;
for(int i=1;i<=n;++i)
if(t[i]==1) q.push(i);
while(!q.empty()){
u=q.front();q.pop();++cnt;
for(int i=g[u];i;i=e[i].nxt)
if((--t[e[i].to]==1))
q.push(e[i].to);
}
}
inline void dfs(int u){
ins[u]=true;
for(int i=g[u];i;i=e[i].nxt)
if(!ins[e[i].to]){
d2[e[i].to]=d2[u]+1;
if(!d1[e[i].to]){
d1[e[i].to]=d1[u]+1;
ans+=1.0/(double)(d1[e[i].to]);
}
else ans+=1.0/(double)(d2[e[i].to])-2.0/(double)(d1[e[i].to]+d2[e[i].to]+cnt-2);
dfs(e[i].to);
}
ins[u]=false;
}
inline void Aireen(){
n=read();
for(int i=1,j,k;i<=n;++i){
j=read()+1;k=read()+1;
addedge(j,k);addedge(k,j);
++t[j];++t[k];
}
cnt=0;toposort();cnt=n-cnt;
for(int i=1;i<=n;++i){
memset(d1,0,sizeof(d1));
memset(d2,0,sizeof(d2));
d1[i]=d2[i]=1;dfs(i);
}
printf("%.8lf\n",ans+n);
}
int main(){
freopen("dac.in","r",stdin);
freopen("dac.out","w",stdout);
Aireen();
fclose(stdin);
fclose(stdout);
return 0;
}