zoukankan      html  css  js  c++  java
  • P4427 [BJOI2018]求和(倍增LCA、树链剖分)

    题目描述

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

    输入格式

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

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

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

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

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

    输出格式

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

    题解:

    注意到k的范围只有50,所以先预处理出每个节点的深度k次前缀和,然后用树上差分的思想求出答案。求LCA用树剖或倍增LCA都可以。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=3e5+100;
    const ll mod=998244353;
    ll h[55][maxn];
    int n,m;
    vector<int> g[maxn];
    
    int father[20][maxn];
    ll db[55][maxn];//预处理所有数的k次
     
    void dfs (int x) {
        for (int i=0;i<g[x].size();i++) {
            int v=g[x][i];
            if (v==father[0][x]) continue;
            father[0][v]=x;
            h[0][v]=h[0][x]+1;
            for (int k=1;k<=50;k++)
                h[k][v]=h[k][x]+db[k][h[0][v]],
                h[k][v]%=mod;
            dfs(v);
        }
    }
    int lca (int x,int y) {
        if (h[0][x]<h[0][y]) swap(x,y);
        for (int i=17;i>=0;i--)
            if (h[0][x]-h[0][y]>>i) x=father[i][x];
        if (x==y) return x;
        for (int i=17;i>=0;i--) 
            if (father[i][x]!=father[i][y]) {
                x=father[i][x];
                y=father[i][y];
            }
        return father[0][x];
    }
    ll getDis (int x,int y,int k) {
        int Lca=lca(x,y);
        int lf=father[0][Lca];
        return (h[k][x]+h[k][y])%mod+2*mod-(h[k][Lca]+h[k][lf]);
    }
    
    int main () {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) {
            db[1][i]=i;
            for (int j=2;j<=50;j++)    
                db[j][i]=db[j-1][i]*db[1][i],
                db[j][i]%=mod;
        }
        for (int i=1;i<n;i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs(1);
        for (int i=1;i<=17;i++)
            for (int j=1;j<=n;j++)
                father[i][j]=father[i-1][father[i-1][j]];
        scanf("%d",&m);
        for (int i=1;i<=m;i++) {
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            printf("%lld
    ",(getDis(x,y,k)+mod)%mod);
        }
    }
  • 相关阅读:
    《CMake实践》笔记二:INSTALL/CMAKE_INSTALL_PREFIX
    《CMake实践》笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE
    《CMake实践》第三部分的示例代码的错误
    利用 autoconf 和 automake 生成 Makefile 文件
    如何安装 罗技“优联技术”无线鼠标、无线键盘?
    make 和 makefile 的关系
    编译器 cc、gcc、g++、CC 的区别
    如何撤销 PhpStorm/Clion 等 JetBrains 产品的 “Mark as Plain Text” 操作 ?
    Linux/Ubuntu tree 命令以树形结构显示文件夹目录结构
    C/C++ 静态链接库(.a) 与 动态链接库(.so)
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/13462848.html
Copyright © 2011-2022 走看看