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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章