7.19T2
小 B 的树
题目背景及题意
小 B 有一颗树,它的形态与 OI 中的树相同,有 n 个节点,n-1 条边,每条边长度为 1 随着时间的流逝这棵树长大了,每条边有 50%的概率长度变为 2
小 B 想知道这棵树期望的直径是多少,可是他太菜了并不会,所以来求教你
输入格式
第一行一个 n 代表树上点的个数
接下来 n-1 行,每行两个整数,表示一条边
输出格式
一个实数,表示直径的期望
数据范围
打包,总共 10 个包,第 i 个包里的 n=i*5+10
sol:学长题解写的很好,自己都能看懂,就是写起来很操蛋(有详细注释,那处写挂的ts了一年)
upd:感觉自己全部在fp
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) {f|=(ch=='-'); ch=getchar();} while(isdigit(ch)) {s=(s<<3)+(s<<1)+(ch^48); ch=getchar();} return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) {putchar('-'); x=-x;} if(x<10) {putchar(x+'0'); return;} write(x/10); putchar((x%10)+'0'); } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=75,M=150; int n,mx[N],sz[N]; /* mx[i]表示包括i在内的以i为根的子树中的最长链 sz[i]表示以i为根的子树大小 */ int tot=0,Next[M],to[M],head[N]; double dp[N][M][M],f[2][M][M][M],ans=0.0; /* dp[i][j][k]表示i的子树,最大深度为j,直径<=k的概率 做的时候按照直径=k做,然后搞一遍前缀和即可 f[i][j][k][l]表示在x的子树中前i个儿子的子树中,最长链为j,次长链为k,直径<=l的概率(因为是从dp转移过来的,直接带前缀和) */ inline void Link(int x,int y) { Next[++tot]=head[x]; to[tot]=y; head[x]=tot; } inline void dfs(int x,int fat) { int e,i,j,k,l,t; sz[x]=1; for(e=head[x];e;e=Next[e]) if(to[e]!=fat) { dfs(to[e],x); sz[x]+=sz[to[e]]; } for(i=0;i<=sz[x]*2;i++) f[t=0][0][0][i]=1.0; for(e=head[x];e;e=Next[e]) if(to[e]!=fat) { int V=to[e]; t^=1; for(i=0;i<=mx[V];i++) for(j=1;j<=(sz[x]*2);j++) dp[V][i][j]+=dp[V][i][j-1];//前缀和 //注意这里j要转移到sz[x]*2而不是sz[V]*2,虽然后面对于V而言没有数值,但在x转移是要用啊!!!!! for(i=0;i<=mx[x];i++) for(j=0;j<=i;j++) for(k=0;k<=sz[x]*2;k++)//最长链,次长链,直径(不一定要求一定直径最长) { double tmp=f[t^1][i][j][k]; f[t^1][i][j][k]=0; //要加入一个新儿子了 for(l=0;l<=mx[V];l++)//枚举V中的最长链长度 { double val=dp[V][l][k]*0.5;//0.5是要分类转移 if(l+1>i) f[t][l+1][i][k]+=tmp*val; else if(l+1>j) f[t][i][l+1][k]+=tmp*val; else f[t][i][j][k]+=tmp*val; if(l+2>i) f[t][l+2][i][k]+=tmp*val; else if(l+2>j) f[t][i][l+2][k]+=tmp*val; else f[t][i][j][k]+=tmp*val; } } mx[x]=max(mx[x],mx[V]+2); } // printf("%d %d %lf %lf ",x,mx[x],f[t][mx[x]][0][mx[x]],f[t][mx[x]][0][mx[x]-1]); for(i=0;i<=mx[x];i++) for(j=0;j<=i;j++) for(k=sz[x]*2;k>=0;k--) { if(k!=0) f[t][i][j][k]-=f[t][i][j][k-1]; dp[x][i][max(i+j,k)]+=f[t][i][j][k]; f[t][i][j][k]=0; } // printf("%lf %lf\n",dp[x][mx[x]][mx[x]],dp[x][mx[x]][mx[x]-1]); } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); int i,j,x,y; R(n); for(i=1;i<n;i++) { R(x); R(y); Link(x,y); Link(y,x); } dfs(1,0); // printf("%lf %lf %lf %lf %lf\n",dp[1][4][4],dp[1][5][5],dp[1][6][6],dp[1][7][7],dp[1][8][8]); // for(i=1;i<=n;i++) cout<<i<<' '<<mx[i]<<' '<<sz[i]<<endl; for(i=0;i<=mx[1];i++) for(j=1;j<=n*2;j++) ans+=dp[1][i][j]*j; printf("%lf\n",ans); return 0; }
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!