【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 <bits/stdc++.h>
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<long long> 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<<j) <= n;j++)
        for (long long i = 1;i <= n;i++)
            father[i][j] = father[father[i][j-1]][j-1];
}
inline long long lca(long long a,long long b) {
    if (dep[a] < dep[b]) swap(a,b);
    for (long long i = 20;i >= 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;
}
posted @ 2018-09-30 22:03  lrj124  阅读(225)  评论(0编辑  收藏  举报