大致题意: 给定一棵树/基环树,随机一点作为起点,且每次随机走向一个没有走过的点,当旁边没有没走过的点时结束行走。每条边有一定边权,求走的总长度的期望。
基环树
应该是一道思维难度不高的题目,树的情况显然是非常套路的,而基环树的情况只要多讨论一个环上的转移即可。
但是,思维难度不高,代码难度却高啊,至少我感觉我的做法非常复杂,调了半个下午才调出来。
首先,对于环上每一个点,我们先遍历其子树求出一个(f_x)表示(x)子树内的贡献总和。
然后,考虑在环上走的情况,显然从(i)走到(i+1)的概率是(frac{1}{deg_i-1})((i)不是起点,(i)到(i-1)同理),但还要注意如果已经在环上绕了一圈,(i)的下一个点是起点,就不能接着在环上走了。
所以在这一过程中只要特殊处理这最后一个点,并维护好其他所有点的贡献总和即可。
至于其他的一些细节问题,由于调得很累,这里就懒得多说了,直接看代码吧。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define DB long double
#define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=v)
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n,m,ee,lnk[N+5];struct edge {int to,nxt,val;}e[2*N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
#undef D
}F;
class CircleTreeDP
{
private:
int cnt,c[2*N+5],p[N+5],D[N+5];DB ans,f[N+5],s[N+5];
DB sum[2*N+5],F[2*N+5],pre[2*N+5],suf[2*N+5];
I int E(CI x,CI y) {for(RI i=lnk[x];i;i=e[i].nxt) if(e[i].to==y) return e[i].val;}
int vis[N+5],d[N+5],fa[N+5];I bool Find(CI x,CI lst=0)//找环
{
vis[x]=1;for(RI i=lnk[x],u,v;i;i=e[i].nxt) if((v=e[i].to)^lst)
{
if(!vis[v]) {if(d[v]=d[fa[v]=x]+1,Find(v,x)) return 1;continue;}
d[u=x]<d[v]&&swap(u,v);W(d[u]^d[v]) p[c[++cnt]=u]=1,u=fa[u];
RI w=v,t=0;W(p[c[++cnt]=u]=1,u^v) u=fa[u],v=fa[v],++t;
W(t) p[c[cnt+(t--)]=w]=1,w=fa[w];return 1;
}return 0;
}
I void DP(CI x,CI lst=0)//DP求出子树内的贡献
{
D[x]=1;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&!p[e[i].to]&&
(DP(e[i].to,x),f[x]+=(s[e[i].to]+=e[i].val),++D[x]);s[x]=D[x]^1?f[x]/(D[x]-1):0;
}
I void Calc(CI x,CI lst=0,DB t=0)//结合子树外的贡献统计答案
{
ans+=(f[x]+t)/D[x];for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
!p[e[i].to]&&(Calc(e[i].to,x,(D[x]^1?(f[x]+t-s[e[i].to])/(D[x]-1):0)+e[i].val),0);
}
public:
I void Tree() {DP(1),--D[1],Calc(1,0),printf("%.5Lf
",ans/n);}//处理树的情况
I void CTree()//处理基环树的情况
{
RI i;DB g,u;for(Find(1),i=1;i<=cnt;++i) DP(c[i]),++D[c[cnt+i]=c[i]];//对环上每个点先DP
for(pre[0]=i=1;i<=(cnt<<1);++i) pre[i]=pre[i-1]/(D[c[i]]-1);//统计前缀概率积
for(suf[cnt<<1|1]=1,i=(cnt<<1);i;--i) suf[i]=suf[i+1]/(D[c[i]]-1);//统计后缀概率积
for(F[1]=(D[c[1]]^2?f[c[1]]/(D[c[1]]-2):0),i=2;i<=(cnt<<1);++i)
sum[i]=sum[i-1]+E(c[i-1],c[i]),F[i]=(D[c[i]]^2?f[c[i]]/(D[c[i]]-2):0)+sum[i];
for(g=0,i=2;i^cnt;++i) g+=pre[i]*(D[c[1]]-1)*(D[c[i]]-2)*F[i];//预处理第1个点的答案
for(i=1;i<=cnt;++i) f[c[i]]+=g+pre[i+cnt-2]/pre[i]*F[i+cnt-1]-sum[i],//更新f
g=g*(D[c[i+1]]-1)+pre[i+cnt-1]/pre[i+1]*(D[c[i+cnt-1]]-2)*F[i+cnt-1]-(D[c[i+1]]-2)*F[i+1];//更新g
for(F[cnt<<1]-=sum[cnt<<1],sum[cnt<<1]=0,i=2*cnt-1;i;--i)
F[i]-=sum[i],sum[i]=sum[i+1]+E(c[i],c[i+1]),F[i]+=sum[i];
for(g=0,i=2*cnt-1;i^(cnt+1);--i) g+=suf[i]*(D[c[cnt<<1]]-1)*(D[c[i]]-2)*F[i];//预处理第n个点的答案
for(i=cnt<<1;i^cnt;--i) f[c[i]]+=g+suf[i-cnt+2]/suf[i]*F[i-cnt+1]-sum[i],//更新f
g=g*(D[c[i-1]]-1)+suf[i-cnt+1]/suf[i-1]*(D[c[i-cnt+1]]-2)*F[i-cnt+1]-(D[c[i-1]]-2)*F[i-1];//更新g
for(i=1;i<=cnt;++i) Calc(c[i]);printf("%.5Lf
",ans/n);//统计答案
}
}T;
int main()
{
RI i,x,y,z;for(F.read(n,m),i=1;i<=m;++i) F.read(x,y,z),add(x,y,z),add(y,x,z);
return m^n?T.Tree():T.CTree(),0;
}