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;
}
原文地址:https://www.cnblogs.com/lnzwz/p/12863328.html