一次查询时间O(lgn)
预处理:
求每一个节点的深度与父节点
int f[N][20],dep[N];
V<pii>h[N];
int n;
inline void dfs(int u, int fa)
{
f[u][0] = fa;
dep[u] = dep[fa] + 1;
for (auto v : h[u])
{
if (v.first == fa)continue;
dfs(v.first, u);
}
}
//
inline int lca(int x, int y)
{
if (dep[x] < dep[y])
swap(x, y);
int d = dep[x] - dep[y];
for(int j=M;j>=0;j--)
if (d & (1 << j))
x = f[x][j];
if (x == y)
return x;
for (int i =M; i >= 0; i--)
if (f[x][i] != f[y][i])
{
x = f[x][i], y = f[y][i];
}
return f[x][0];
}
int main()
{
int q;
scanf("%d%d", &n, &q);
for (int i = 1; i < n; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
h[u].push_back({ v,w });
h[v].push_back({ u,w });
}
dfs(1, 0);
for (int j = 1; j<=M; j++)
for (int u = 1; u <= n; u++)
{
f[u][j] = f[f[u][j - 1]][j - 1];
}
while (q--)
{
int u, v; scanf("%d%d", &u, &v);
printf("%d\n", lca(u, v));
}
}
前缀和lca (后缀和 lca 同理)
int f[ N ];
f[ i ] 表示 1~i个点的 lca
f[ 1 ]=a[ 1 ];
for(int i=2;i<=n;i++)
f[i]=lca( f[ i-1 ] ,a[ i ]);
树上两点间的路径最小值
int f[N][20], dep[N];
V<pii>h[N];
int n;
int res[N][20];
inline void dfs(int x, int fa)
{
f[x][0] = fa;
dep[x] = dep[fa] + 1;
for (pii w : h[x])
{
if (w.first == fa)continue;
res[w.first][0] = w.second;
dfs(w.first, x);
}
}
inline int lca(int x, int y)
{
int ans = 0x3f3f3f3f;
if (dep[x] < dep[y])
swap(x, y);
int d = dep[x] - dep[y];
for (int j = 18; j >= 0; j--)
if (d & (1 << j))
{
ans = min(ans, res[x][j]);
x = f[x][j];
}
if (x == y)
return ans;
for (int i = 18; i >= 0; i--)
if (f[x][i] != f[y][i])
{
ans = min(ans, min(res[x][i], res[y][i]));
x = f[x][i], y = f[y][i];
}
return ans = min(ans, min(res[x][0], res[y][0]));
}
int main()
{
int q;
scanf("%d%d", &n, &q);
for (int i = 1; i < n; i++)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
h[u].push_back({ v,w });
h[v].push_back({ u,w });
}
dfs(1, 0);
for (int j = 1; j <= 18; j++)
for (int u = 1; u <= n; u++)
{
f[u][j] = f[f[u][j - 1]][j - 1];
res[u][j] = min(res[u][j - 1], res[f[u][j - 1]][j - 1]);
}
while (q--)
{
int u, v; scanf("%d%d", &u, &v);
printf("%d\n", lca(u, v));
}
}
树上差分:
int f[N][20], dep[N];
V<int>h[N];
int n;
// tag[i] 记录差分 val[i]记录 每次差分后 为了消除其他影响 而记录的值
int a[N],tag[N],val[N];
inline void dfs1(int x, int fa)
{
f[x][0] = fa;
dep[x] = dep[fa] + 1;
for (auto v : h[x])
{
if (v == fa)continue;
dfs1(v, x);
}
}
//
inline int Lca(int x, int y)
{
if (dep[x] < dep[y])
swap(x, y);
int d = dep[x] - dep[y];
for (int j = 18; j >= 0; j--)
if (d & (1 << j))
x = f[x][j];
if (x == y)
return x;
for (int i = 20; i >= 0; i--)
if (f[x][i] != f[y][i])
{
x = f[x][i], y = f[y][i];
}
return f[x][0];
}
// 遍历 tag数组 对于差分数字求和
void dfs2(int u, int fa)
{
for (auto v : h[u])
{
if (v == fa)continue;
dfs2(v, u);
tag[u] += tag[v];
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
h[u].push_back(v);
h[v].push_back(u);
}
dfs1(1, 0);
for (int j = 1; j <= 18; j++)
for (int u = 1; u <= n; u++)
{
f[u][j] = f[f[u][j - 1]][j - 1];
}
for (int i = 2; i <= n; i++)
{
int u = a[i - 1], v = a[i];
int lca = Lca(u, v);
tag[u]++;
tag[v]++;
// 在 lca这个节点以上的节点中 没有了差分贡献 但自身值需要加1
tag[lca] -= 2;
val[lca]++;
// u->v->x 路径中 下一次时候 v又被加过一次 此次需要-1
//同时 题意中 在访问最后an的时候 也需要-1
val[v]--;
}
dfs2(1, 0);
for (int i = 1; i <= n; i++)
cout << val[i] + tag[i] << "\n";
}