题目大意
有$n$个点的前边权为$0$的树,你要加入$S$边权总量,可以为分数,使得当前树的直径最小。
题目分析
题目过于毒瘤,导致于最后$1$个小时一直在做此题,没想到真的只是一个结论一样的东西。
我们不要想十分复杂,我们发现数的直径两端都会在度数为$1$的点上,就是叶子节点。然后呢我们就可以把此题转换成让两两叶子节点直接距离相等且最短时是多少。
就比如当此时我们便让$(1,3),(1,4),(1,6),(3,6),(3,4),(4,6)$距离都是一样的,答案是$0.5$。
所以现在我们要做的是将树的直径平分,这是一个定值,也就是答案,我们发现怎么去构造呢,易看图发现有些边权值为$0$$(2,5)$,这样会使每两个叶子节点只会走两个权值不为$0$的,也就是两个叶子节点挨着边。
所以我们可以发现答案为$frac{(n-1) imes k}{C_{ans}^2}$,其中$ans$为叶子节点个数,$k$为一共需要加到$k$这个权值,而$n-1$是因为每条叶子节点挨着的边都遍历过$n-1$遍,整理的答案为$frac{2 imes k}{ans}$
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=500001; int n,k,du[MAXN],ans; int main(){ n=read(),k=read(); for(int i=1;i<n;i++)du[read()]++,du[read()]++; for(int i=1;i<=n;i++) if(du[i]==1) ans++; printf("%.10lf",2*1.0*k/ans); }