基情链接
解题思路
LCA没什么好说的,对于这类树上路径的题向来lca比较好处理。值得一提的是,k次方是各边分别k次方。由于k比较小,其实就不用管他,处理k棵树即可。
一句话题意:求树上两个点之间的所有点“深度”之和。
我们就可以分解一下这一段路径,其实就是a到lca与b到lca,我们用一个前缀和,储存,差分思想再相减即可。
LCA被多减了1次,我们需加回来。
还有,此题需要取模,还是比较坑。
对于求LCA,我们有许多方法,这里就有大家喜闻乐见的暴力,即在相同深度,一起往上爬,直到走到相同点。同时还有倍增,即,每次成倍爬树,找到不相等的最小深度的点,在走一步即到了LCA,第三种方法就是tarjan。有一个好处是可以直接求出所有,就非常好用。
暴力通常很慢,这道题我用的倍增。
定义dp[i][j]表示从i点向上走2^j步所到达的点。
显然有。
其实就相当于把2^j拆成先走2^j - 1 再走2^j - 1
给出一个模板
void dfs(int step){
vis[step] = 1;
for (int i = 0;i < G[step].size();i ++){
if (!vis[G[step][i]]){
dep[G[step][i]] = dep[step] + 1;
dp[G[step][i]][0] = step;
dfs(G[step][i]);
}
}
}
void build(){
dep[1] = 0;
dfs(1);
dp[1][0] = 1;
for (int i = 1;i <= 31;i ++){
for (int j = 1;j <= n;j ++){
dp[j][i] = dp[dp[j][i - 1]][i - 1];
}
}
}
void mov(int &u,int v){
for (int i = 30;i >= 0;i --){
if (dep[dp[u][i]] >= v)
u = dp[u][i];
}
}
int solve(int u,int v){
if (dep[u] < dep[v])
mov(v,dep[u]);
if (dep[u] > dep[v])
mov(u,dep[v]);
if (u == v)
return u;
for (int i = 30;i >= 0;i --){
if (dp[u][i] != dp[v][i]){
u = dp[u][i];
v = dp[v][i];
}
}
return dp[u][0];
}
dfs其实就是预处理出dp初值以及深度,因为我们的在线LCA都是要在同一深度进行操作。
同时如果刚开始深度不相同,我们则需要用mov移动到相同深度,然后开始爬树。
我们采取的方式是一直往上面爬,如果爬到的点相同,则不一定是LCA,不一定是最近的公共祖先,因此,我们一直爬到不相同的点,最后,往上面再爬一下即是LCA。
这道题再加一个前缀和以及处理k棵树即可。耗时比较久,应该有些优化。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<vector>
#include<algorithm>
#define mod 998244353
using namespace std;
int n,m;
bool vis[300005];
int dp[300005][31],dep[300005];
long long sum[300005][55];
vector <int>G[300005];
long long qkpow(long long x,long long y){
long long ans = 1;
while (y){
if (y & 1)
ans = ans * x % mod;
x = x * x % mod;
y /= 2;
}
return ans;
}
void dfs(int step){
vis[step] = 1;
for (int i = 0;i < G[step].size();i ++){
if (!vis[G[step][i]]){
dep[G[step][i]] = dep[step] + 1;
for (int j = 1;j <= 50;j ++)
sum[G[step][i]][j] = (qkpow(dep[G[step][i]],j)+ sum[step][j]) % mod;
dp[G[step][i]][0] = step;
dfs(G[step][i]);
}
}
}
void build(){
/*for (int i = 2;i <= 50;i ++)
sum[1][i] = 1;*/
dfs(1);
dp[1][0] = 1;
for (int j = 1;j <= 20;j ++){
for (int i = 1;i <= n;i ++){
dp[i][j] = dp[dp[i][j - 1]][j - 1];
}
}
}
void mov(int &u,int v){
for (int i = 20;i >= 0;i --){
if (dep[dp[u][i]] >= dep[v]){
u = dp[u][i];
}
}
}
int solve(int u,int v){
if (dep[u] > dep[v])
mov(u,v);
if (dep[u] < dep[v])
mov(v,u);
if (u == v)
return u;
for (int i = 20;i >= 0;i --){
if (dp[u][i] != dp[v][i]){
u = dp[u][i];
v = dp[v][i];
}
}
return dp[u][0];
}
int main(){
scanf ("%d",&n);
for (int i = 1;i < n;i ++){
int a,b;
scanf ("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
build();
scanf("%d",&m);
while (m --){
int x1,x2,k;
scanf ("%d%d%d",&x1,&x2,&k);
int lca = solve(x1,x2);
printf("%lld
",(sum[x1][k] - sum[lca][k] + sum[x2][k] - sum[dp[lca][0]][k] + 2 * mod ) % mod);
}
}