[LCA]求和(bzoj5293)

基情链接

解题思路

LCA没什么好说的,对于这类树上路径的题向来lca比较好处理。值得一提的是,k次方是各边分别k次方。由于k比较小,其实就不用管他,处理k棵树即可。

一句话题意:求树上两个点之间的所有点“深度”之和。

我们就可以分解一下这一段路径,其实就是a到lca与b到lca,我们用一个前缀和,储存,差分思想再相减即可。

LCA被多减了1次,我们需加回来。

还有,此题需要取模,还是比较坑。

对于求LCA,我们有许多方法,这里就有大家喜闻乐见的暴力,即在相同深度,一起往上爬,直到走到相同点。同时还有倍增,即,每次成倍爬树,找到不相等的最小深度的点,在走一步即到了LCA,第三种方法就是tarjan。有一个好处是可以直接求出所有,就非常好用。

暴力通常很慢,这道题我用的倍增。

定义dp[i][j]表示从i点向上走2^j步所到达的点。

显然有dp[i][j] = dp[dp[i][j - 1]][j - 1]

其实就相当于把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);
	}
}
原文地址:https://www.cnblogs.com/lover-fucker/p/13566676.html