题目链接:Hihocoder-1167
主要思路:
多画几棵树可以看出两条路径相交时只有一条路径的LCA落在另一条路径上,故你可以求每个LCA落在几条路径上(若两条路径LCA相同,这时候就会算两遍,故将LCA从路径上剔除,特判即可)。
求每个LCA落在几条路径上就可以用树上差分。
AC代码:
#include<cstdio>
#include<algorithm>
#include<vector>
#define lowbit(x) x&-x
#define M 100005
using namespace std;
struct E {
int to,nx;
} edge[M<<1];
int tot,head[M];
void Addedge(int a,int b) {
edge[++tot].to=b;
edge[tot].nx=head[a];
head[a]=tot;
}
struct Path {
int from,to;
bool operator <(const Path &x)const {
return to<x.to;
}
} way[M];
struct p100 { //若两条路径相交则必有一条的LCA落在另一条上
int n,m;
int sz[M],fa[M],son[M],dep[M];
void dfs(int now) {
sz[now]=1;
son[now]=0;
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
if(nxt==fa[now])continue;
fa[nxt]=now;
dep[nxt]=dep[now]+1;
dfs(nxt);
if(sz[son[now]]<sz[nxt])son[now]=nxt;
sz[now]+=sz[nxt];
}
}
int top[M];
void redfs(int now) {
if(son[now]) {
int nxt=son[now];
top[nxt]=top[now];
redfs(nxt);
}
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
if(nxt==fa[now]||nxt==son[now])continue;
top[nxt]=nxt;
redfs(nxt);
}
}
int LCA(int a,int b) {//跳重链求LCA
while(top[a]!=top[b]) {
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=fa[top[a]];
}
return dep[a]<dep[b]?a:b;
}
long long ans;
void ansdfs(int now) {
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
if(nxt==fa[now])continue;
ansdfs(nxt);
sum[now]+=sum[nxt];//sum即为当前点在几条线段上
}
ans+=1ll*sum[now]*cnt[now]+1ll*cnt[now]*(cnt[now]-1ll)/2;//两个LCA相同会多算出情况,故特判
}
int sum[M],cnt[M];
void solve(int n,int m) {
ans=0;
this->n=n;
this->m=m;
dfs(1);
top[1]=1;
redfs(1);
for(int i=1; i<=m; i++) {
sum[way[i].from]++;//差分
sum[way[i].to]++;
int x=LCA(way[i].from,way[i].to);
sum[x]-=2;//不把LCA算上去
cnt[x]++;
}
ansdfs(1);
printf("%lld\n",ans);
}
} P100;
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1; i<n; i++) {
int a,b;
scanf("%d%d",&a,&b);
Addedge(a,b);
Addedge(b,a);//***建反向边
}
for(int i=1; i<=m; i++) {
scanf("%d%d",&way[i].from,&way[i].to);
if(way[i].from>way[i].to)swap(way[i].from,way[i].to);
}
P100.solve(n,m);
}