M_sea

洛谷4427 [BJOI2018]求和
题目描述master 对树上的求和非常感兴趣。他生成了一棵有根树,并且希望多次询问这棵树上一段路径上所有节点深度的...
扫描右侧二维码阅读全文
05
2018/08

洛谷4427 [BJOI2018]求和

题目描述

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

传送门

算法

暴力前缀和预处理+快速幂+LCA+树上差分。

首先Dfs,预处理LCA的同时计算出前缀和$s[][]$。

$s[i][j]$表示1~i的路径上所有点深度的j次方和。

然后对于每个查询,直接利用前缀和树上差分,搞出答案。

总时间复杂度$O(nk)$,能过。

细节见代码。

代码

#include <bits/stdc++.h>
#define re register
typedef int mainint;
#define int long long
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=(X<<3)+(X<<1)+c-'0',c=getchar();
    return X*w;
}

const int MOD=998244353;

inline int FastPow(int x,int k) {
    int rt=1;
    while (k) {
        if (k&1) rt=rt*x%MOD;
        x=x*x%MOD;
        k>>=1;
    }
    return rt;
}

struct Edge { int v,nxt; };
Edge e[600010];
int head[300010],cnt=0;

inline void addEdge(int u,int v) {
    e[++cnt].v=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}

int f[300010][21];
int s[300010][60];
int dep[300010];

inline void Dfs(int u,int fa) {
    dep[u]=dep[fa]+1; f[u][0]=fa;
    for (re int i=1;i<=50;i++) s[u][i]=s[fa][i]+FastPow(dep[u],i);
    for (re int i=1;(1<<i)<=dep[u];i++)
        f[u][i]=f[f[u][i-1]][i-1];
    for (re int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v;
        if (v!=fa) Dfs(v,u);
    }
}

inline int LCA(int a,int b) {
    if (dep[a]<dep[b]) swap(a,b);
    for (re int i=20;~i;i--)
        if (dep[f[a][i]]>dep[b]) a=f[a][i];
    if (dep[a]!=dep[b]) a=f[a][0];
    for (re int i=20;~i;i--)
        if (f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
    if (a!=b) a=f[a][0],b=f[b][0];
    return a;
}

mainint main() {
    int n=read();
    for (re int i=1;i<n;i++) {
        int x=read(),y=read();
        addEdge(x,y);
        addEdge(y,x);
    }
    dep[0]=-1; Dfs(1,0);
    int m=read();
    while (m--) {
        int i=read(),j=read(),k=read();
        int lca=LCA(i,j);
        printf("%lld\n",(s[i][k]+s[j][k]-s[lca][k]-s[f[lca][0]][k])%MOD);
    }
    return 0;
}
最后修改:2018 年 11 月 09 日 05 : 02 PM

发表评论