题意:
给你一张n个点n-1或n条边的带权无向图。从每个点出发一直走下去,不能重复经过某个点。问走过的路径长度的数学期望是多少? N<=100000。图中至多只有一个环,并且环长不超过20。
题解: 首先考虑是一颗树的情况: 首先随便取一个点为根(例如1号点),做树状动规。 设点i走向它的子树的期望长度为F[i],i的子节点为s1、s2……sk。 设D[i] = ∑( F[sj] + edge(i,sj) ) ,那么F[i] = D[i] / k。 设P[i]为i作为起点的期望长度,那么P[1]=F[1],Du[i]是节点i的度数。 再进行一次DFS,如果y是x的子节点,P[x]已经算出。 设节点y向x走对期望的贡献是other,other=(P[x]*Du[x]-edge(x,y)-F[x])/(Du[x]-1)+edge(x,y) P[y]=(other+D[y])/Du[y] 这样通过两次DFS,就可以算出所有点的P值。 但是这个题可能是一颗基环树。 首先可以通过dfs找出这个环,如果去掉这个环,那就是一些子树构成的森林。 首先用前面提到的树形动规的方法处理这些子树,求出D和F。 然后注意到环上的点很少,我们就依次让环上的每个点作为起点计算一遍。 对于环上的点x,设calc_pre(x)为从x向环上左侧走得到的期望长度,calc_nxt(x)为从x向环上右侧走得到的期望长度。 sx为x的孩子。 那么P[x]=(∑(F[sx]+edge(x,sx)+calc_pre(x)+calc_nxt(x))/Du(x)。 其中calc_pre,calc_nxt可以O(环上点个数)复杂度求出。 于是就可以再次dfs算出所有点的P。 ans=(∑P[i])/n
/**************************************************************
Problem: 2878
User: zrts
Language: C++
Result: Accepted
Time:588 ms
Memory:10748 kb
****************************************************************/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
//by zrt
//problem:
using namespace std;
typedef long long LL;
const int inf(0x3f3f3f3f);
const double eps(1e-9);
bool oncircle[100005];
int du[100005];
int fa[100005];
int H[100005],X[200005],P[200005],tot;
double E[200005];
inline void add(int x,int y,int z){//add an edge
P[++tot]=y;X[tot]=H[x];H[x]=tot;E[tot]=z;
}
int n,m;
double F[100005];//for son E
double D[100005];// F * |son|
double p[100005];// ans
int num[100005];// |son|
int pre[100005];
double wpre[100005];
bool type;
void treedp1(int x){//Dp the tree first
for(int i=H[x];i;i=X[i]){
if(P[i]==fa[x]||oncircle[P[i]]) continue;
fa[P[i]]=x;
treedp1(P[i]);
D[x]+=F[P[i]]+E[i];num[x]++;
}
if(num[x])
F[x]=D[x]/num[x];
else F[x]=0;
}
void treedp2(int x,double edge){//Dp the tree second | calc the P
double other;
if(!fa[fa[x]]) {
if(!type){
if(num[fa[x]]>1) other=(p[fa[x]]*(num[fa[x]]+0)-edge-F[x])/(num[fa[x]]-1)+edge;
else other=edge;
}else{
other=(p[fa[x]]*(num[fa[x]]+2)-edge-F[x])/(num[fa[x]]+1)+edge;
}
}
else other=(p[fa[x]]*(num[fa[x]]+1)-edge-F[x])/num[fa[x]]+edge;
p[x]=(other+D[x])/(num[x]+1);
for(int i=H[x];i;i=X[i]){
if(P[i]==fa[x]||oncircle[P[i]]) continue;
treedp2(P[i],E[i]);
}
}
vector<int> circle;
bool ok;
int nxt[100005];double wnxt[100005];
void findcircle(int x,int fa){
for(int i=H[x];i;i=X[i]){
if(P[i]==fa) continue;
if(pre[P[i]]){
ok=1;
int k=x;
pre[P[i]]=x;
wpre[P[i]]=E[i];
do{
oncircle[k]=1;
circle.push_back(k);
k=pre[k];
}while(k!=P[i]);
oncircle[k]=1;
circle.push_back(k);
}else{
pre[P[i]]=x;wpre[P[i]]=E[i];
findcircle(P[i],x);
if(ok) return;
}
}
}
double calc_nxt(int now,int end){
double ret=wnxt[now];
now=nxt[now];
int extra=0;
if(nxt[now]!=end) ret+=calc_nxt(now,end)/(du[now]-1);
else extra=-1;
for(int i=H[now];i;i=X[i]){
if(oncircle[P[i]]) continue;
ret+=(E[i]+F[P[i]])/(du[now]-1+extra);
}
return ret;
}
double calc_pre(int now,int end){
double ret=wpre[now];
now=pre[now];
int extra=0;
if(pre[now]!=end) ret+=calc_pre(now,end)/(du[now]-1);
else extra=-1;
for(int i=H[now];i;i=X[i]){
if(oncircle[P[i]]) continue;
ret+=(E[i]+F[P[i]])/(du[now]-1+extra);
}
return ret;
}
int main(){// the main function
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d%d",&n,&m);
for(int i=0,x,y,z;i<m;i++){
scanf("%d%d%d",&x,&y,&z);
du[x]++;
du[y]++;
add(x,y,z);
add(y,x,z);
}
if(m==n-1){
treedp1(1);
p[1]=F[1];
for(int i=H[1];i;i=X[i]){
treedp2(P[i],E[i]);
}
double ans=0;
for(int i=1;i<=n;i++){
ans+=p[i];
}
printf("%.5f\n",ans/n);
}else{
type=1;
pre[1]=-1;
findcircle(1,-1);
for(int now=0;now<circle.size();now++){
nxt[pre[circle[now]]]=circle[now];
wnxt[pre[circle[now]]]=wpre[circle[now]];
}
for(int now=0;now<circle.size();now++){
for(int i=H[circle[now]];i;i=X[i]){
if(oncircle[P[i]]) continue;
treedp1(P[i]);
p[circle[now]]+=(F[P[i]]+E[i])/du[circle[now]];
num[circle[now]]++;
}
}
for(int now=0;now<circle.size();now++){
p[circle[now]]+=(calc_pre(circle[now],circle[now])+calc_nxt(circle[now],circle[now]))/du[circle[now]];
}
for(int now=0;now<circle.size();now++){
for(int i=H[circle[now]];i;i=X[i]){
if(oncircle[P[i]]) continue;
fa[P[i]]=circle[now];
treedp2(P[i],E[i]);
}
}
double ans=0;
for(int i=1;i<=n;i++){
ans+=p[i];
}
printf("%.5f\n",ans/n);
}
return 0;
}