题目大意:有一棵n个点的树,给出m个数对,每次从一个点出发到另一个点,问每条边被访问的次数
2<=n<=1e5;0<=m<=1e5
思路:树上边差分题,先将边权下放到点权,令每条边的终点的点权为边权,然后要统计边的访问次数,就将起点和终点的计数分别+1,因为起点和终点的lca为这个子树的根,所以计数应该为0,也就要-2,这样该子树以外的点的计数就都不变
//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int head[N], cnt;
int fa[N][31], d[N];
int num[N];
int n;
int ans[N];
struct Edge
{
int v, next, id;
}e[N * 2];//邻接链表存图
void addedge(int u, int v,int c)
{
e[++cnt].v = v;
e[cnt].next = head[u];
e[cnt].id = c;//要记录边的编号
head[u] = cnt;
}
void init()
{//图的初始化
cnt = 0;
for (int i = 1; i <= n; i++)
{
head[i] = -1;
}
}
void dfs(int u, int faa)
{//倍增法求lca的初始化
fa[u][0] = faa, d[u] = d[faa] + 1;//求出每个点的父结点和深度
for (int i = 1; i <= 30; i++)
{//求不同距离下的父结点
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (v == faa)//避免重复遍历
continue;
dfs(v, u);
}
}
int lca(int x, int y)
{//倍增法求lca
if (d[x] < d[y])
{//确保x是更深的点
swap(x, y);
}
for (int i = 30; i >= 0; i--)
{
if (d[fa[x][i]] >= d[y])
{//令x和y的深度一样
x = fa[x][i];
}
}
if (x == y)
return x;
for (int i = 30; i >= 0; i--)
{
if (fa[x][i] != fa[y][i])
{//同步上跳
x = fa[x][i], y = fa[y][i];
}
}
return fa[x][0];
}
void add(int u, int faa)
{//对子树求前缀和
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].v;
if (v == faa)
{
continue;
}
add(v, u);
ans[e[i].id] += num[v];//点权回归到边权
num[u] += num[v];//前缀和累加
}
}
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
cin >> n;
init();
for (int i = 1; i <= n-1; i++)
{
int u, v;
cin >> u >> v;
addedge(u, v, i);
addedge(v, u, i);
}
dfs(1, 0);
int m;
cin >> m;
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
int l = lca(u, v);
num[u]++;
num[v]++;
num[l] -= 2;
}
add(1, 0);
for (int i = 1; i <= n - 1; i++)
{
cout << ans[i] << " ";
}
return 0;
}
,