【CF235D】Graph Game

题目

题目链接:https://codeforces.com/problemset/problem/235/D
求基环树随机点分治总遍历次数期望。
基环树随机点分治步骤:

  1. 遍历当前分治区域所有点一次。
  2. 在当前分治区域随机选择一个点 \(x\)
  3. \(x\) 删掉,产生的所有连通块递归处理。

\(n\leq 3000\)

思路

考虑枚举两个点 \(x,y\)\(y\) 能给 \(x\) 的贡献,其实就是当 \(x\) 作为分治中心的时候,\(y\)\(x\) 连通的期望。
如果在树上,设 \(x,y\) 两点之间有 \(d\) 个点,很显然 \(x\)\(y\) 连通当前仅当 \(x\)\(x\)\(y\) 的链上第一个被删除的点,它的概率也就是 \(\frac{1}{d}\)
这道题给出的是一棵基环树,如果两个点之间的路径只有 \(1\) 条,那么贡献就和树上一样。如果两点之间的路径有两条,容斥一下,设两条路径各自的点数分别为 \(d1,d2\),两条路径的并集的点数为 \(d3\),那么这一组点的贡献就是 \(\frac{1}{d1}+\frac{1}{d2}-\frac{1}{d3}\)
时间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=3010;
int n,m,tot,head[N],id[N],siz[N],rk[N],top[N],deg[N],dep[N],a[N];
double ans;

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void topsort()
{
	queue<int> q;
	for (int i=1;i<=n;i++)
		if (deg[i]==1) q.push(i);
	while (q.size())
	{
		int u=q.front(); q.pop();
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			deg[v]--;
			if (deg[v]==1) q.push(v);
		}
	}
}

void dfs1(int x,int fa,int tp)
{
	top[x]=tp; dep[x]=dep[fa]+1;
	id[x]=++tot; rk[tot]=x; siz[x]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa && deg[v]<=1)
		{
			dfs1(v,x,tp);
			for (int j=id[x];j<id[x]+siz[x];j++)
				for (int k=id[v];k<id[v]+siz[v];k++)
					ans+=2.0/(dep[rk[j]]+dep[rk[k]]-2*dep[x]+1);
			siz[x]+=siz[v];
		}
	}
}

void dfs2(int x,int fa,int tp)
{
	a[x]=++m;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (deg[v]>=2 && v!=fa && v!=tp)
			return (void)(dfs2(v,x,tp));
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for (int i=1,x,y;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		x++; y++;
		add(x,y); add(y,x);
		deg[x]++; deg[y]++;
	}
	topsort();
	tot=0;
	for (int i=1;i<=n;i++)
		if (deg[i]>=2) dfs1(i,0,i);
	for (int i=1;i<=n;i++)
		if (deg[i]>=2) { dfs2(i,0,i); break; }
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if (top[i]!=top[j])
			{
				int d1=abs(a[top[i]]-a[top[j]])-1,d2=m-d1-2;
				ans+=1.0/(dep[i]+dep[j]+d1)+1.0/(dep[i]+dep[j]+d2);
				ans-=1.0/(dep[i]+dep[j]+m-2);
			}
	printf("%.12lf",ans+n);
	return 0;
}
posted @ 2021-08-22 19:31  stoorz  阅读(65)  评论(0编辑  收藏  举报