CF235D
题意:求基环树随机点分治次数期望
首先,这道题的本质是给分治中心随机排列。
考虑分治中心x与y连通的概率,若x到y是一条链,就要求x到y上的所有点,在x之后被删除。
把这些概率加到一起就是答案。
如果这条链包含的点数为a,容易证出此时是1/a。
(共有\(n!\)种情况,满足条件的有\(\frac{n!}{a}\)种)
如果x到y的路径上经过环,设除去环的部分点数为a,环的两部分点数分别为b和c。
只要除去环上任意一侧的点,都在x之前被删除即可,这部分为\(\frac{1}{a+b}\),加上\(\frac{1}{a+c}\),但是,还有所有的点都在x之前被删除的情况,这种情况会被算两次,需要减掉,就是再减去\(\frac{1}{a+b+c}\)。
\(O(n^2)\)枚举点对进行计算即可。
代码:
#include <stdio.h>
int fr[3010],ne[6010],v[6010],bs=0;
void addb(int a,int b)
{
v[bs]=b;
ne[bs]=fr[a];
fr[a]=bs++;
}
int bk[3010],sd[3010],hu[3010],fa[3010],sl=0;
void dfs1(int u,int f)
{
bk[u]=1;fa[u]=f;
sd[u]=sd[f]+1;
for(int i=fr[u];i!=-1;i=ne[i])
{
if(v[i]==f)continue;
if(bk[v[i]])
{
if(sd[v[i]]<sd[u])
{
int t=u;
while(1)
{
hu[sl++]=t;
if(t==v[i])break;
t=fa[t];
}
}
}
else
dfs1(v[i],u);
}
}
int hd[3010],ro[3010],lc[3010][3010],wz[3010];
void dfs2(int u,int f,int rt)
{
fa[u]=f;ro[u]=rt;
sd[u]=sd[f]+1;
for(int i=fr[u];i!=-1;i=ne[i])
{
if(v[i]==f||hd[v[i]])
continue;
dfs2(v[i],u,rt);
}
}
int dfs3(int a,int b)
{
if(a==b)
return lc[a][b]=a;
if(lc[a][b])
return lc[a][b];
if(sd[a]>sd[b])
lc[a][b]=dfs3(fa[a],b);
else
lc[a][b]=dfs3(a,fa[b]);
return lc[a][b];
}
int getdis(int a,int b)
{
int l=dfs3(a,b);
return sd[a]+sd[b]-sd[l]*2+1;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
fr[i]=-1;
for(int i=0;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
a+=1;b+=1;
addb(a,b);addb(b,a);
}
dfs1(1,0);
for(int i=0;i<sl;i++)
{
hd[hu[i]]=1;
wz[hu[i]]=i;
}
for(int i=0;i<sl;i++)
dfs2(hu[i],0,hu[i]);
double ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(ro[i]==ro[j])
ans+=1.0/getdis(i,j);
else
{
int a=sd[i]+sd[j];
int b=wz[ro[i]]-wz[ro[j]];
if(b<0)b=-b;
b-=1;int c=sl-2-b;
ans+=1.0/(a+b);
ans+=1.0/(a+c);
ans-=1.0/(a+b+c);
}
}
}
printf("%.10lf",ans);
return 0;
}