基环树套路题。(然而各种错误调了好久233)
当$m=n-1$时,原图是一棵树。
先以任意点为根做$dp$,求出从每一个点出发,然后只往自己子树里走时路径的期望长度。
接着再把整棵树再扫一遍,求出从每一个点出发时路径的期望长度。
最后再统计一遍答案即可。
当$m=n$时,原图是一棵基环树。
先找到图中那唯一的一个环,然后以环上的每一个节点为根在此节点的子树内做$dp$。
环上节点的数量很少,$dp$之后直接暴力算出从每一个环上节点出发时路径的期望长度。
然后把每一个环上节点的子树扫一遍,求出从每一个点出发时路径的期望长度。
最后再统计一遍答案即可。
#include<iostream> #include<cstdio> #include<fstream> using namespace std; struct edge { int last; int end; int weight; }e[200005]; int n=0,m=0,root=0; int ne=1,cnt[100005],note[100005]; int tot=0,ring[100005]; int ringdis[100005]; bool flag=false,vis[100005],inring[100005]; double ans=0,f[100005],d[100005]; void NewEdge(int u,int v,int w) { ne++; e[ne].last=note[u]; e[ne].end=v; e[ne].weight=w; note[u]=ne; } void dfs(int x,int fx) { cnt[x]=f[x]=0; for(int i=note[x];i;i=e[i].last) { if(e[i].end==fx||inring[e[i].end]) continue; dfs(e[i].end,x),cnt[x]++; } if(cnt[x]==0) return; for(int i=note[x];i;i=e[i].last) { if(e[i].end==fx||inring[e[i].end]) continue; f[x]+=e[i].weight+f[e[i].end]; } f[x]/=cnt[x]; } void dfss(int x,int fx,int w) { if(x==root&&!inring[x]) d[x]=f[x]; else if(x!=root) { d[x]=f[x]*cnt[x]/(cnt[x]+1); if(fx==root&&cnt[fx]==1) d[x]+=(double)w/(cnt[x]+1); else if(fx==root) d[x]+=((d[fx]*cnt[fx]-f[x]-w)/(cnt[fx]-1)+w)/(cnt[x]+1); else d[x]+=((d[fx]*(cnt[fx]+1)-f[x]-w)/cnt[fx]+w)/(cnt[x]+1); } for(int i=note[x];i;i=e[i].last) { if(e[i].end==fx||inring[e[i].end]) continue; dfss(e[i].end,x,e[i].weight); } } void dfsss(int x,int fe) { if(flag) return; vis[x]=true,ring[++tot]=x; for(int i=note[x];i;i=e[i].last) { if(i==(fe^1)) continue; if(flag) break; if(vis[e[i].end]) { int st=0,ed=tot,h=e[i].end; for(int i=1;i<=tot;i++) if(ring[i]==h) {st=i;break;} for(int i=st;i<=ed;i++) { inring[ring[i]]=true; for(int j=note[ring[i]];j;j=e[j].last) if(e[j].end==((i==ed)?ring[st]:ring[i+1])) {ringdis[i]=e[j].weight;break;} } for(int i=st;i<=ed;i++) dfs(ring[i],0); for(int i=st;i<=ed;i++) { int x=ring[i]; d[x]=f[x]*cnt[x]/(cnt[x]+2); int u=i,w=0; double p=1; for(;;) { p/=cnt[ring[u]]+((u==i)?2:1),w=ringdis[u]; u=((u+1>ed)?st:(u+1)); if(((u+1>ed)?st:(u+1))==i) {d[x]+=p*(f[ring[u]]+w); break;} else d[x]+=p*(f[ring[u]]*cnt[ring[u]]/(cnt[ring[u]]+1)+w); } u=i,w=0,p=1; for(;;) { p/=cnt[ring[u]]+((u==i)?2:1),w=ringdis[(u-1<st)?ed:(u-1)]; u=((u-1<st)?ed:(u-1)); if(((u-1<st)?ed:(u-1))==i) {d[x]+=p*(f[ring[u]]+w); break;} else d[x]+=p*(f[ring[u]]*cnt[ring[u]]/(cnt[ring[u]]+1)+w); } } for(int i=st;i<=ed;i++) cnt[ring[i]]+=2,root=ring[i],dfss(root,0,0); flag=true; break; } dfsss(e[i].end,i); } tot--; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u=0,v=0,w=0; scanf("%d%d%d",&u,&v,&w); NewEdge(u,v,w); NewEdge(v,u,w); } if(m==n-1) root=1,dfs(root,0),dfss(root,0,0); else dfsss(1,0); for(int i=1;i<=n;i++) ans+=(double)1/n*d[i]; printf("%.5f",ans); return 0; }
Luogu P2081 [NOI2012]迷失游乐园 | 期望 DP 基环树
原文:https://www.cnblogs.com/wozaixuexi/p/11216153.html