Description

兵库县位于日本列岛的中央位置,北临日本海,南面濑户内海直通太平洋,中央部位是森林和山地,与拥有关西机场的大阪府比邻而居,是关西地区面积最大的县,是集经济和文化于一体的一大地区,是日本西部门户,海陆空交通设施发达。濑户内海沿岸气候温暖,多晴天,有日本少见的贸易良港神户港所在的神户市和曾是豪族城邑“城下町”的姬路市等大城市,还有以疗养地而闻名的六甲山地等。
兵库县官方也大力发展旅游,为了方便,他们在县内的N个旅游景点上建立了n-1条观光道,构成了一棵图论中的树。同时他们推出了M条观光线路,每条线路由两个节点x和y指定,经过的旅游景点就是树上x到y的唯一路径上的点。保证一条路径只出现一次。
你和你的朋友打算前往兵库县旅游,但旅行社还没有告知你们最终选择的观光线路是哪一条(假设是线路A)。这时候你得到了一个消息:在兵库北有一群丧心病狂的香菜蜜,他们已经选定了一条观光线路(假设是线路B),对这条路线上的所有景点都释放了【精神污染】。这个计划还有可能影响其他的线路,比如有四个景点1-2-3-4,而【精神污染】的路径是1-4,那么1-3,2-4,1-2等路径也被视为被完全污染了。
现在你想知道的是,假设随便选择两条不同的路径A和B,存在一条路径使得如果这条路径被污染,另一条路径也被污染的概率。换句话说,一条路径被另一条路径包含的概率。

Input

第一行两个整数N,M
接下来N-1行,每行两个数a,b,表示A和B之间有一条观光道。
接下来M行,每行两个数x,y,表示一条旅游线路。

Output

所求的概率,以最简分数形式输出。

Sample Input

5 3
1 2
2 3
3 4
2 5
3 5
2 5
1 4

Sample Output

1/3
样例解释
可以选择的路径对有(1,2),(1,3),(2,3),只有路径1完全覆盖路径2。

题目大意:

  一棵n个节点的树上有m条路径,求所有路径覆盖其他路径之和。

题解:

  纱布出题人卡我内存。

  设路径的两个端点为第一、二关键字,即x,y,考虑某条边i覆盖另外一条边j的条件,i的第一关键字在j第一关键字子树中,第二关键字同理,即j的第一关键字在i的第一关键字到lca(i.x,i.y)路径,j的第二关键字在i.x到lca或i.y到lca之间,用树上查分的思想,就是满足第一关键字在i.x到根或i.y的路径上,第二关键字在lca(i.x,i.y)到i.x,i.y之间的总数减去第一关键字在lca以及lca的父亲节点到根节点的路径,第二关键字同上的边。

  然后在树上每个节点开一颗主席树,记录所有第一关键字在当前节点到根节点路径上的边的第二关键字的分布情况,这个分布情况也要满足可以查分,显然用欧拉括号序即可。

  然后。。。千万不要给root【0】建一个空树。。。。。。

 #include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=;
inline int read();
int n,m;
struct edges{
int v;int last;
}edge[N<<];int head[N] ,cnt;
inline void add_edge(int u,int v){
edge[++cnt].v=v;
edge[cnt].last=head[u];
head[u]=cnt;
}
int beg[N],end[N],deep[N];
int f[N][];
void dfs(int x){
beg[x]=++cnt;
for(int i=;(<<i)<=deep[x];i++)
f[x][i]=f[f[x][i-]][i-];
for(int i=head[x];i;i=edge[i].last){
if(f[x][]!=edge[i].v){
deep[edge[i].v]
=deep[x]+;
f[edge[i].v][]=x;
dfs(edge[i].v);
}
}
end[x]=++cnt;
}
inline int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
int t=deep[x]-deep[y];
for(int i=;t;i++)
if((<<i)&t)
t^=(<<i),x=f[x][i];
if(x!=y){
for(int i=;i>=;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
x=f[x][];
}
return x;
}
//LCA************************************************
struct lines{
int v,last;
}line[N];int last[N], Cnt;
inline void add_line(int u,int v){
line[++Cnt].v=v;line[Cnt].last=last[u];
last[u]=Cnt;
}
//************************************************
struct Tree{
Tree *lc,*rc;int sum;
}*root[N*],tree[N*];int num;
inline int query (Tree *u,Tree *v,Tree *Lca,Tree *Lca_f,int x,int y,int L,int R){
if(x<=L&&R<=y) return u->sum+v->sum-Lca->sum-Lca_f->sum;
int mid=L+R>>;
int sum=;
if(y>mid) sum+=query(u->rc,v->rc,Lca->rc,Lca_f->rc,x,y,mid+,R);
if(x<=mid) sum+=query(u->lc,v->lc,Lca->lc,Lca_f->lc,x,y,L,mid);
return sum;
}
void build(Tree *&u,int l,int r){
u=tree+num;
u->sum=;
if(l==r){return ;}
int mid=l+r>>;
build(u->lc,l,mid);
build(u->rc,mid+,r);
}
void update(Tree *&u,Tree *p,int l,int r,int op,int w){
tree[++num]=*p;
u=tree+num;
u->sum+=w;
if(l==r) return ;
int mid=l+r>>;
if(op>mid) update(u->rc,p->rc,mid+,r,op,w);
else update(u->lc,p->lc,l,mid,op,w);
}
int tot;
void dfs_make(int x){
root[x]=root[f[x][]];
for(int i=last[x];i;i=line[i].last){
update(root[x],root[x],,cnt,beg[line[i].v],);
update(root[x],root[x],,cnt,end[line[i].v],-);
}
for(int i=head[x];i;i=edge[i].last){
if(f[x][]!=edge[i].v)
dfs_make(edge[i].v);
}
}
inline ll gcd(ll a,ll b){
return b==?a:gcd(b,a%b);
}
int main(){
n=read(),m=read();
for(int i=,u,v;i<n;i++){
u=read(),v=read();
add_edge(u,v);add_edge(v,u);
}
cnt=;
dfs();
build(root[],,cnt);
for(int i=,a,b;i<=m;i++){
a=read(),b=read();
add_line(a,b);
}
dfs_make();
ll ans=;
for(int i=;i<=n;i++) for(int j=last[i];j;j=line[j].last){ int u=i,v=line[j].v;
int Lca=lca(u,v);
ans+=query(root[u],root[v],root[Lca],root[f[Lca][]],beg[Lca],beg[u],,cnt)
+query(root[u],root[v],root[Lca],root[f[Lca][]],beg[Lca],beg[v],,cnt)
-query(root[u],root[v],root[Lca],root[f[Lca][]],beg[Lca],beg[Lca],,cnt);
ans--; }
ll aaa=(ll)(m)*(ll)(m-)/;
ll t=gcd(ans,aaa);
printf("%lld/%lld\n",ans/(int )t,aaa/t);
}
inline int read(){
int s=,f=;char ch=getchar();
for(;ch<''||ch>'';ch=getchar())if(ch=='-')f=-;
for(;ch>=''&&ch<='';ch=getchar())s=s*+(ch^);
return s*f;
}
05-08 15:51