一.非树边与树边构成图的性质

二.本题的题解

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=N*2;
int n,m;
int e[M],ne[M],h[N],idx;
int depth[N];
int fa[N][17]; //17是这样算的:点数是100000,log2(100000)大概是17
int q[N];
int d[N]; //存每一个点差分的一个值
int ans; //求的是每一棵子树的总和是多少
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void bfs()
{
memset(depth,0x3f,sizeof depth);
depth[0]=0;
depth[1]=1;
int hh=0,tt=-1;
q[++tt]=1;
while(hh<=tt)
{
int t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(depth[j]>depth[t]+1)
{
depth[j]=depth[t]+1;
q[++tt]=j;
fa[j][0]=t;
for(int k=1;k<=16;k++)
{
fa[j][k]=fa[fa[j][k-1]][k-1];
}
}
}
}
}
int lca(int a,int b)
{
if(depth[a]<depth[b])
{
swap(a,b);
}
for(int k=16;k>=0;k--)
{
if(depth[fa[a][k]]>=depth[b])
{
a=fa[a][k];
}
}
if(a==b) return a;
for(int k=16;k>=0;k--)
{
if(fa[a][k]!=fa[b][k])
{
a=fa[a][k];
b=fa[b][k];
}
}
return fa[a][0];
}
//树上差分后求类似于前缀和
int dfs(int u,int father)
{
int res=d[u];
/*
如果在一维差分中,就类似于求
d[]数组的前缀和数组,
枚举到u时,就要加上d[u];
但是之前的部分,我们可以用递归去求。
就比如下面的s
*/
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j==father) continue;
int s=dfs(j,u);
if(s==0) ans+=m;
else if(s==1) ans++;
res+=s;
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
for(int i=0;i<n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
bfs(); //预处理倍增数组
for(int i=0;i<m;i++)//读入m个非树边
{
int a,b;
scanf("%d%d",&a,&b);
int p=lca(a,b);
//差分(o(1)修改)
d[a]++;
d[b]++;
d[p]-=2;
}
dfs(1,-1); //dfs返回的是每一棵子树的和是多少
printf("%d\n",ans);
return 0;
}