#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using
namespace
std;
namespace
Fio{
inline
int
getc
(){
static
const
int
L=1<<15;
static
char
buf[L],*S=buf,*T=buf;
if
(S==T){T=(S=buf)+
fread
(buf,1,L,stdin);
if
(S==T)
return
EOF;}
return
*S++;
}
inline
bool
digit(
int
x){
return
x>=
'0'
&&x<=
'9'
;}
template
<
typename
T>
inline
void
Get(T&x){
int
c;
while
(!digit(c=
getc
()));x=c-
'0'
;
while
(digit(c=
getc
()))x=(x<<1)+(x<<3)+c-
'0'
;
}
}
#define N 100010
int
n,m;
int
head[N],next[N<<1],end[N<<1];
inline
void
addedge(
int
a,
int
b){
static
int
q=1;end[q]=b,next[q]=head[a],head[a]=q++;}
inline
void
make(
int
a,
int
b){addedge(a,b),addedge(b,a);}
int
pa[N][17],dep[N],in[N],out[N],tclock;
void
dfs(
int
x,
int
fa){
in[x]=++tclock;
for
(
int
j=head[x];j;j=next[j])
if
(end[j]!=fa){pa[end[j]][0]=x,dep[end[j]]=dep[x]+1,dfs(end[j],x);}
out[x]=tclock;
}
inline
int
lca(
int
x,
int
y){
if
(dep[x]<dep[y])swap(x,y);
for
(
int
i=16;i>=0;--i)
if
(dep[pa[x][i]]>=dep[y])x=pa[x][i];
if
(x==y)
return
x;
for
(
int
i=16;i>=0;--i)
if
(pa[x][i]!=pa[y][i])x=pa[x][i],y=pa[y][i];
return
pa[x][0];
}
#define M 100010
int
x[M],y[M],Lca[M];
vector<
int
>v[N];
int
root[N<<1];
struct
SegmentTree{
struct
Node{
int
l,r,siz;
}S[3000000];
int
cnt;
inline
void
reset(){cnt=0;}
inline
int
newnode(){
int
q=++cnt;S[q].l=S[q].r=S[q].siz=0;
return
q;}
int
Newversion(
int
Last,
int
tl,
int
tr,
int
ins){
int
q=newnode();S[q]=S[Last],++S[q].siz;
if
(tl==tr)
return
q;
int
mid=(tl+tr)>>1;
if
(ins<=mid)S[q].l=Newversion(S[Last].l,tl,mid,ins);
else
S[q].r=Newversion(S[Last].r,mid+1,tr,ins);
return
q;
}
inline
int
Query(
int
q,
int
tl,
int
tr,
int
dl,
int
dr){
if
(dl>dr)
return
0;
if
(dl<=tl&&tr<=dr){
return
S[q].siz;}
int
mid=(tl+tr)>>1;
if
(dr<=mid)
return
Query(S[q].l,tl,mid,dl,dr);
else
if
(dl>mid)
return
Query(S[q].r,mid+1,tr,dl,dr);
else
return
Query(S[q].l,tl,mid,dl,mid)+Query(S[q].r,mid+1,tr,mid+1,dr);
}
}Seg;
struct
Path{
int
x,y;
Path(){}
Path(
int
_x,
int
_y):x(_x),y(_y){}
}sav[M<<1];
inline
bool
cmp1(
const
Path&A,
const
Path&B){
return
in[A.x]<in[B.x];}
inline
bool
cmp2(
const
Path&A,
const
Path&B){
return
in[A.y]<in[B.y];}
int
seq[M<<1];
long
long
res;
void
calcstep1(){
register
int
i;
int
lans,rans,L,R,mid;
for
(
int
Root=1;Root<=n;++Root){
int
num=(
int
)v[Root].size();
if
(!num)
continue
;
for
(i=1;i<=num;++i){sav[i]=Path(x[v[Root][i-1]],y[v[Root][i-1]]);
if
(in[sav[i].x]>in[sav[i].y])swap(sav[i].x,sav[i].y);}
sort(sav+1,sav+num+1,cmp1);
Seg.reset();
for
(i=1;i<=num;++i)root[i]=Seg.Newversion(root[i-1],1,n,in[sav[i].y]);
for
(i=1;i<=num;++i)seq[i]=in[sav[i].x];
for
(i=1;i<=num;++i){
if
(in[sav[i].x]>seq[num]||out[sav[i].x]<seq[1])
continue
;
for
(L=1,R=num;L<R;){
mid=(L+R)>>1;
if
(seq[mid]>=in[sav[i].x])R=mid;
else
L=mid+1;
}lans=L;
for
(L=1,R=num;L<R;){
mid=(L+R+1)>>1;
if
(seq[mid]>out[sav[i].x])R=mid-1;
else
L=mid;
}rans=L;
res+=Seg.Query(root[rans],1,n,in[sav[i].y],out[sav[i].y])-Seg.Query(root[lans-1],1,n,in[sav[i].y],out[sav[i].y])-1;
}
}
}
void
calcstep2(){
register
int
i;
int
lans,rans,L,R,mid,num=0;
for
(i=1;i<=m;++i){
if
(x[i]==Lca[i]||y[i]==Lca[i]){sav[++num]=Path(x[i],y[i]);
if
(in[sav[num].x]>in[sav[num].y])swap(sav[num].x,sav[num].y);}
}
sort(sav+1,sav+num+1,cmp2);
Seg.reset();
for
(i=1;i<=num;++i)root[i]=Seg.Newversion(root[i-1],1,n,in[sav[i].x]);
for
(i=1;i<=num;++i)seq[i]=in[sav[i].y];
for
(i=1;i<=num;++i){
if
(in[sav[i].y]>seq[num]||out[sav[i].y]<seq[1])
continue
;
for
(L=1,R=num;L<R;){
mid=(L+R)>>1;
if
(seq[mid]>=in[sav[i].y])R=mid;
else
L=mid+1;
}lans=L;
for
(L=1,R=num;L<R;){
mid=(L+R+1)>>1;
if
(seq[mid]>out[sav[i].y])R=mid-1;
else
L=mid;
}rans=L;
res+=Seg.Query(root[rans],1,n,1,in[sav[i].x])+Seg.Query(root[rans],1,n,out[sav[i].x]+1,n);
res-=Seg.Query(root[lans-1],1,n,1,in[sav[i].x])+Seg.Query(root[lans-1],1,n,out[sav[i].x]+1,n);
res--;
}
}
void
calcstep3(){
register
int
i;
int
lans,rans,L,R,mid,num=0;
for
(i=1;i<=m;++i)
if
(x[i]!=Lca[i]&&y[i]!=Lca[i])sav[++num]=Path(Lca[i],x[i]),sav[++num]=Path(Lca[i],y[i]);
sort(sav+1,sav+num+1,cmp2);
Seg.reset();
for
(i=1;i<=num;++i)root[i]=Seg.Newversion(root[i-1],1,n,in[sav[i].x]);
for
(i=1;i<=num;++i)seq[i]=in[sav[i].y];
for
(i=1;i<=m;++i){
if
(x[i]!=Lca[i]&&y[i]!=Lca[i])
continue
;
if
(in[x[i]]>in[y[i]])swap(x[i],y[i]);
if
(in[y[i]]>seq[num]||out[y[i]]<seq[1])
continue
;
for
(L=1,R=num;L<R;){
mid=(L+R)>>1;
if
(seq[mid]>=in[y[i]])R=mid;
else
L=mid+1;
}lans=L;
for
(L=1,R=num;L<R;){
mid=(L+R+1)>>1;
if
(seq[mid]>out[y[i]])R=mid-1;
else
L=mid;
}rans=L;
res+=Seg.Query(root[rans],1,n,1,in[x[i]])+Seg.Query(root[rans],1,n,out[x[i]]+1,n);
res-=Seg.Query(root[lans-1],1,n,1,in[x[i]])+Seg.Query(root[lans-1],1,n,out[x[i]]+1,n);
if
(x[i]==y[i])res-=(
int
)v[x[i]].size();
}
}
long
long
gcd(
long
long
a,
long
long
b){
return
(!b)?a:gcd(b,a%b);}
int
main(){
Fio::Get(n),Fio::Get(m);
register
int
i,j;
int
a,b;
for
(i=1;i<n;++i){
Fio::Get(a),Fio::Get(b);make(a,b);
}
dep[1]=1,dfs(1,-1);
for
(j=1;j<=16;++j)
for
(i=1;i<=n;++i)pa[i][j]=pa[pa[i][j-1]][j-1];
for
(i=1;i<=m;++i){
Fio::Get(x[i]),Fio::Get(y[i]),Lca[i]=lca(x[i],y[i]);
if
(Lca[i]!=x[i]&&Lca[i]!=y[i])v[Lca[i]].push_back(i);
}
calcstep1();
calcstep2();
calcstep3();
if
(res==0)
puts
(
"0/1"
);
else
{
long
long
total=(
long
long
)m*(m-1)/2;
long
long
Gcd=gcd(res,total);res/=Gcd,total/=Gcd;
printf
(
"%lld/%lld"
,res,total);
}
return
0;
}