#include<bits/stdc++.h>
using namespace std;
//见https://blog.csdn.net/sky980114/article/details/69038382
//观察发现从某一个节点到达另一个节点的路径期望值等于路径中所有可能被遍历的边数之和,因为深搜的特点导致了遍历完一整棵树才能走
// 准则2可以转化成准则1,因为准则2终点固定
int n,x[100001],y[100001],son[100001]={0};
long long sum=0,sumx=0,sumy=0,lf[100001];//最后再除,因为精度太高了
vector<int> g[100001];//不用存权重,方便
void dfs1(int k,int f){
//用于求子节点数
for(int i=0;i<g[k].size();i++){
//遍历vector
if(g[k][i]!=f){
dfs1(g[k][i],k);
son[k]+=son[g[k][i]];
son[k]++;//如果没有这个那么都是0,求的是 子节点 数,因为是子节点数少了一个所以+1
//因为只有非叶子才能++,导致最下面的分叉是叶子节点 son=0 在++
lf[k]+=lf[g[k][i]];//这是包括当前点和子节点的x和,因为初始化了
}
}
}
//不用for用dfs因为需要子树关系,lf
void dfs2(int k,int f){
//具体求值
//预处理,全部安照方式2,求所有不是k的点到k的和,因为按照方式2需要确定终点,所以。。
sum+=(n-1-son[k])*(sumx-x[k])*y[k];
for(int i=0;i<g[k].size();i++){
if(g[k][i]!=f){
dfs2(g[k][i],k);//对子节点求完
//修改,对方式1的进行修正,利用lf
sum-=(n-1-son[k])*lf[g[k][i]]*y[k];
sum+=(son[g[k][i]]+1)*lf[g[k][i]]*y[k];
}
}
}
int main() {
cin>>n;
for(int i=0;i<n-1;i++){
int x,y;
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i];
sumx+=x[i];
sumy+=y[i];
lf[i]=x[i];//lf用于优化,因为发现 开始:某个点 结束:他的父节点,对于这个点的子节点,期望和这个点到父节点相同,这样可以优化
}
dfs1(1,-1);
dfs2(1,-1);
printf("%.11f\n",1.0*sum/sumx/sumy);
return 0;
}