【BJOI2018】求和 - 倍增LCA

题目描述

$master$ 对树上的求和非常感兴趣。他生成了一棵有根树,并且希望多次询问这棵树上一段路径上所有节点深度的$k$次方和,而且每次的$k$可能是不同的。此处节点深度的定义是这个节点到根的路径上的边数。他把这个问题交给了$pupil$,但$pupil$ 并不会这么复杂的操作,你能帮他解决吗?

输入输出格式

输入格式:

第一行包含一个正整数n,表示树的节点数。

之后n-1行每行两个空格隔开的正整数i, j ,表示树上的一条连接点$i$和点$j$的边。

之后一行一个正整数,表示询问的数量。

之后每行三个空格隔开的正整数i, j, k,表示询问从点i到点j的路径上所有节点深度的k次方和。由于这个结果可能非常大,输出其对998244353取模的结果。

树的节点从1开始标号,其中1号节点为树的根。

输出格式:

对于每组数据输出一行一个正整数表示取模后的结果。

思路

对$k=1...50$全部预处理出来,然后就是LCA模板题了

#include 
using namespace std;
const long long maxn = 300000 + 10;
const long long MOD =  998244353;
long long n,m,dep[maxn],father[maxn][25],d[maxn][51];
vector edges[maxn];
inline long long quickpow(long long x,long long y) {
	long long ans = 1;
	for (;y;y >>= 1,x = x*x%MOD) if (y&1) ans = ans*x%MOD;
	return ans;
}
inline void dfs(long long now,long long fa) {
	dep[now] =  dep[fa]+1;
	for (long long j = 1;j <= 50;j++) d[now][j] = quickpow(dep[now],j)+d[fa][j];
	for (long long i = 0;i < edges[now].size();i++)
		if (edges[now][i]  != fa) {
			dfs(edges[now][i],now);
			father[edges[now][i]][0] = now;
		}
}
inline void init() {
	for (long long j = 1;(1<= 0;i--)
		if (dep[father[a][i]] >= dep[b]) a = father[a][i];
	if (a == b) return a;
	for (long long i = 20;i >= 0;i--)
		if (father[a][i] != father[b][i]) {
			a = father[a][i];
			b = father[b][i];
		}
	return father[a][0];
}
int main() {
	scanf("%lld",&n);
	for (long long i = 1,u,v;i < n;i++) {
		scanf("%lld%lld",&u,&v);
		edges[u].push_back(v);
		edges[v].push_back(u);
	}
	dep[1] = -1;
	father[1][0] = 1;
	dfs(1,1);
	init();
	scanf("%lld",&m);
	while (m--) {
		long long a,b,k,LCA;
		scanf("%lld%lld%lld",&a,&b,&k);
		LCA = lca(a,b);
		printf("%lld\n",((d[a][k]+d[b][k])-(d[LCA][k]+d[father[LCA][0]][k]))%MOD);
	}
	return 0;
}

你可能感兴趣的:(【BJOI2018】求和 - 倍增LCA)