BZOJ2878 NOI2012迷失游乐园(树形dp+环套树+概率期望)
考虑树的部分分怎么做。令f[i]为i向子树内走的期望路径长度,转移比较显然。算答案时先把其父亲的答案弄好就可以统计自己的答案了。
环套树也类似。树里直接dp,对环上点暴力考虑环上的每条路径,算完后再在树里统计答案。
说起来不是很难。事实上想清楚了也确实不是很难。
不明白为什么不管啥题我都能把代码写的贼长。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 100010 int n,m,p[N],degree[N],nxtlen[N],t=-1; int dfn[N],low[N],top=0,cnt=0,tot=0,circle[N]; bool flag[N<<1],iscircle[N<<1]; double f[N],g[N],ans=0; struct data{int to,nxt,len; }edge[N<<1]; void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;} void dfs(int k,int from) { for (int i=p[k];~i;i=edge[i].nxt) if (edge[i].to!=from&&!iscircle[edge[i].to]) { dfs(edge[i].to,k); degree[k]++; f[k]+=f[edge[i].to]+edge[i].len; } if (degree[k]) f[k]/=degree[k]; } void getans(int k,int from) { for (int i=p[k];~i;i=edge[i].nxt) if (edge[i].to!=from) { g[edge[i].to]=(f[edge[i].to]*degree[edge[i].to]+edge[i].len+(g[k]*(degree[k]+(k>1))-f[edge[i].to]-edge[i].len)/(degree[k]-(k==1)))/(degree[edge[i].to]+1); getans(edge[i].to,k); } } void getans2(int k,int from) { for (int i=p[k];~i;i=edge[i].nxt) if (edge[i].to!=from&&!iscircle[edge[i].to]) { g[edge[i].to]=(f[edge[i].to]*degree[edge[i].to]+edge[i].len+(g[k]*(degree[k]+1+(k==from))-f[edge[i].to]-edge[i].len)/(degree[k]+(k==from)))/(degree[edge[i].to]+1); getans2(edge[i].to,k); } } void tarjan(int k,int from) { dfn[k]=low[k]=++cnt; for (int i=p[k];~i;i=edge[i].nxt) if (edge[i].to!=from) { if (!dfn[edge[i].to]) tarjan(edge[i].to,k),low[k]=min(low[k],low[edge[i].to]); else low[k]=min(low[k],dfn[edge[i].to]); if (low[edge[i].to]>dfn[k]) flag[i]=flag[i^1]=1; } } void findcircle(int k) { circle[++tot]=k;iscircle[k]=1; for (int i=p[k];~i;i=edge[i].nxt) if (!iscircle[edge[i].to]&&!flag[i]) findcircle(edge[i].to); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2878.in","r",stdin); freopen("bzoj2878.out","w",stdout); #endif n=read(),m=read(); memset(p,255,sizeof(p)); for (int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); addedge(x,y,z),addedge(y,x,z); } if (m==n-1) { dfs(1,1); g[1]=f[1]; if (degree[1]>1) getans(1,1); else g[edge[p[1]].to]=(f[edge[p[1]].to]*degree[edge[p[1]].to]+edge[p[1]].len)/(degree[edge[p[1]].to]+1),getans(edge[p[1]].to,1); for (int i=1;i<=n;i++) ans+=g[i]; } else { for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i,i); for (int i=1;i<=n;i++) { for (int j=p[i];~j;j=edge[j].nxt) if (!flag[j]) {findcircle(i);break;} if (tot) break; } for (int i=1;i<=tot;i++) for (int j=p[circle[i]];~j;j=edge[j].nxt) if (edge[j].to==circle[i%tot+1]) nxtlen[i]=edge[j].len; for (int i=1;i<=tot;i++) dfs(circle[i],circle[i]); for (int i=1;i<=tot;i++) { double P=1; int x=i%tot+1,sum=nxtlen[i]; while (x!=i) { if (degree[circle[x]]) { g[circle[i]]+=P*degree[circle[x]]/(degree[circle[x]]+(x%tot+1!=i))*(f[circle[x]]+sum); P/=(degree[circle[x]]+1); } else if (x%tot+1==i) g[circle[i]]+=P*sum; sum+=nxtlen[x]; x=x%tot+1; } } for (int i=1;i<=tot;i++) { double P=1; int x=i>1?i-1:tot,sum=nxtlen[x]; while (x!=i) { if (degree[circle[x]]) { g[circle[i]]+=P*degree[circle[x]]/(degree[circle[x]]+((x>1?x-1:tot)!=i))*(f[circle[x]]+sum); P/=(degree[circle[x]]+1); } else if ((x>1?x-1:tot)==i) g[circle[i]]+=P*sum; x=x>1?x-1:tot; sum+=nxtlen[x]; } g[circle[i]]+=f[circle[i]]*degree[circle[i]]; g[circle[i]]/=degree[circle[i]]+2; getans2(circle[i],circle[i]); } for (int i=1;i<=n;i++) ans+=g[i]; } printf("%.5lf",ans/n); return 0; }