个点、有边权的基环树,随机游走,不经过重复的点,知道不能走。求期望路径长度。
先看树怎么求。
令 表示从 开始向下走的期望路径长度,则有
再令 表示从 开始向上的期望长度, 代表 的父亲, 为 到 的路径长度,则有
还要特判当 且 的情况,此时 。
如此,树上的 50pts 就得到了。
考虑基环树与树的不同。
其实对于每个点,他们的 数组的转移方式不会变,对于每个不在环上的点,他们 的转移方式也不会变。
但是,若 时,。
由于环上的点只有 20 个,那么在环上的点可以直接搜。这个点开始,不能走向他子树,其他都可以走,然后再 dfs 一遍即可求出他的 。
每个点对答案的贡献是
其中 是环上的点的集合,若数据是树,则树的根的贡献是 。时间是 。
警钟敲烂:用 topo 排序求环时,顺便把 给算了的,没有将环上的 除一个 ,搞完之后还要单独除。
#include<bits/stdc++.h>
#define db double
using namespace std;
const int N=4e5+500;
struct node
{
int u,v,w,nxt;
}e[N];
int n,m,in[N],son[N],head[N],inque[N],cnt,vis[N],fa[N];
db f[N],g[N],ans;
void topo()
{
for(int i=1;i<=cnt;i++)++in[e[i].u];
queue<int>q;
for(int i=1;i<=n;i++)if(in[i]==1)q.push(i);
while(!q.empty())
{
int x=q.front();q.pop();inque[x]=1;
if(son[x])f[x]/=son[x];
for(int i=head[x];i;i=e[i].nxt)
{
int v=e[i].v;
--in[v];
if(in[v]==1)q.push(v);
if(!inque[v])
{
++son[v];fa[x]=v;
f[v]+=f[x]+e[i].w;
}
}
}
}
db dfs(int x,int p)
{
vis[x]=1;int siz=0;db ans=0;
for(int i=head[x];i;i=e[i].nxt)
{
int v=e[i].v;
if((x==p&&fa[v]==x)||vis[v])continue;
ans+=dfs(v,p)+e[i].w;++siz;
}
vis[x]=0;
if(!siz)return 0;
return ans/siz;
}
void get_g(int x)
{
if(in[x]>1)g[x]=dfs(x,x);
for(int i=head[x];i;i=e[i].nxt)
{
int v=e[i].v;
if(fa[v]!=x)continue;
if(!fa[x])
{
if(n==m)g[v]=e[i].w+(f[x]*son[x]-f[v]-e[i].w+2*g[x])/(son[x]+1);
else
{
if(son[x]==1)g[v]=e[i].w;
else g[v]=e[i].w+(f[x]*son[x]-f[v]-e[i].w)/(son[x]-1);
}
}
else g[v]=e[i].w+(f[x]*son[x]+g[x]-f[v]-e[i].w)/son[x];
get_g(v);
}
}
main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
e[++cnt]=(node){u,v,w,head[u]};head[u]=cnt;
e[++cnt]=(node){v,u,w,head[v]};head[v]=cnt;
}
topo();
for(int i=1;i<=n;i++)if(!inque[i]&&son[i])f[i]/=son[i];
for(int i=1;i<=n;i++)if(!fa[i])get_g(i);
for(int i=1;i<=n;i++)
{
if(!inque[i])ans+=(f[i]*son[i]+2*g[i])/(son[i]+2);
else if(fa[i])ans+=(f[i]*son[i]+g[i])/(son[i]+1);
else ans+=f[i];
}
cout<<fixed<<setprecision(6)<<ans/n;
}