首页 > 其他分享 >树上前缀和

树上前缀和

时间:2022-10-04 15:55:19浏览次数:71  
标签:dep ch 前缀 int sum fa logN 树上

sum[i]表示节点i到根节点的权值总和。
如果是点权,x,y路径上的和为sum[x]+sum[y]-sum[lca]-sum[fa[lca]]
如果是边权,x,y路径上的和为sum[x]+sum[y]-2*sum[lca]

边前缀和例题Loj #10134.Dis

#include <bits/stdc++.h>
using namespace std;

const int N = 1e4+5;

int n , m , sum[N] , logN , dep[N] , fa[N][15];
vector<pair<int,int>> e[N];

int read(){
    int x = 0 , f = 1 , ch = getchar();
    while( (ch < '0' || ch > '9') && ch != '-' ) ch = getchar();
    if( ch == '-' ) f = -1 , ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x * f;
}

void dfs( int x ){
    for( auto [v,w] : e[x] ){
        if( dep[v] ) continue;
        dep[v] = dep[x] + 1 , fa[v][0] = x , sum[v] = sum[x] + w;
        for( int i = 1 ; i <= logN ; i ++ )
            fa[v][i] = fa[ fa[v][i-1] ][ i-1 ];
        dfs(v);
    }
}

int lca( int x , int y ){
    if( dep[x] > dep[y] ) swap( x , y );
    for( int i = logN ; i >= 0 ; i -- )
        if( dep[ fa[y][i] ] >= dep[x] ) y = fa[y][i];
    if( x == y ) return x;
    for( int i = logN ; i >= 0 ; i -- )
        if( fa[x][i] != fa[y][i] ) x = fa[x][i] , y = fa[y][i];
    return fa[x][0];
}


int main(){
    n = read() , m = read() ,logN = (int)log2(n)+1;
    for( int i = 2 , u , v , w ; i <= n ; i ++ )
        u = read() , v = read() , w = read() , e[u].push_back( {v,w} ) , e[v].push_back( {u,w} );
    dep[1] = 1 , dfs(1);
    for( int u , v ; m ; m -- ){
        u = read() , v = read();
        cout << sum[u] + sum[v] - 2 * sum[ lca(u,v) ] << "\n";
    }
}

点前缀和例题Luogu P4427

// Loj 2491
// 一颗树根节点是 1 , 点权就是深度的 k 次方
// m次询问,每次问(u,v)路径上点权之和
// k 每次都不同但是取值范围只有[1,50]
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 3e5+5 , mod = 998244353;
int n , sum[N][55] , fa[N][20] , dep[N] , logN;
vector<int> e[N];

int read(){
    int x = 0 , f = 1 , ch = getchar();
    while( (ch < '0' || ch > '9') && ch != '-' ) ch = getchar();
    if( ch == '-' ) f = -1 , ch = getchar();
    while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
    return x * f;
}

void dfs( int x ){
    for( auto v : e[x] ){
        if( v == fa[x][0] ) continue;
        dep[v] = dep[x] + 1 , fa[v][0] = x;
        for( int i = 1 , val = 1; i <= 50 ; i ++ )
            val = val * dep[v]% mod , sum[v][i] = (val + sum[x][i]) % mod;
        for( int i = 1 ; i <= logN ; i ++ )
            fa[v][i] = fa[ fa[v][i-1] ][i-1];
        dfs(v);
    }
}

int lca( int x , int y ){
    if( dep[x] > dep[y] ) swap( x , y );
    for( int i = logN ; i >= 0 ; i -- )
        if( dep[ fa[y][i] ] >= dep[x] ) y = fa[y][i];
    if( x == y ) return x;
    for( int i = logN ; i >= 0 ; i -- )
        if( fa[x][i] != fa[y][i] ) x = fa[x][i] , y = fa[y][i];
    return fa[x][0];
}

int32_t main(){
    n = read() , logN = (int)log2(n)+1;
    for( int i = 2 , u , v ; i <= n ; i ++ )
        u = read() , v = read() , e[u].push_back(v) , e[v].push_back(u);
    dep[1] = 0;
    dfs( 1 );
    for( int m = read() , u , v , k , t ; m ; m -- ){
        u = read() , v = read() , k = read() , t = lca( u , v );
        cout << (sum[u][k]+sum[v][k]-sum[t][k]-sum[fa[t][0]][k]+2*mod)%mod << "\n";
    }
    return 0;
}

标签:dep,ch,前缀,int,sum,fa,logN,树上
From: https://www.cnblogs.com/PHarr/p/16753887.html

相关文章

  • 如何推前缀和式子
    我们设\(\operatorname{f}_k(n)=\sum\limits_{i=1}^{n}i^k\)如果已知\(\operatorname{f}_{k-1}(n)\),如何推导至\(\operatorname{f}_k(n)\)?首先发现:\[\operatorna......
  • 【树上背包】洛谷 P4516 [JSOI2018] 潜入行动
    P4516[JSOI2018]潜入行动省选/NOI-、树上背包计数题意略设状态为\(dp[u][j][0/1][0/1]\),u点子树放了j个装置,u点有没有放装置,u点有没有被监听的方案数。对......
  • 删除名字含有特定前缀的git仓库分支
    我想保留一个仓库中以特定字符串为前缀的分支,还想按照commit时间保留同一前缀的指定数量的分支,删除分支的脚本如下:#!/usr/bin/envpython#-*-coding:utf8-*-#coding:u......
  • LOJ6681. yww 与树上的回文串
    LOJ6681.yww与树上的回文串题意:给定一棵边上带01权值的树,求有多少对\((x,y)\)满足\(x<y\)且\(x\)到\(y\)路径上的边权拼起来是回文串。\(n\leq5\times10^4......
  • 中缀表达式转后缀、前缀记录
    波兰表示法与逆波兰表示法它们都是对表达式的记法,因此也被称为前缀记法、后缀记法。它们之间的区别在于运算符相对与操作数的位置不同:前缀表达式的运算符位于与其相关的操......
  • P2671 [NOIP2015 普及组] 求和(洛谷前缀和
    P2671[NOIP2015普及组]求和1//(x+z)*(num[x]+num[z])=2//(x1+x2)*(y1+y2)+(x1+x3)*(y1+y3)+(x2+x3)*(y2+y3)3//=x1*(y1*(n-2)+y1+y2+...+yn)4//+x2*(y2*......
  • 304. 二维区域和检索 - 矩阵不可变 (二维前缀和)
    304.二维区域和检索-矩阵不可变-力扣(LeetCode)根据原始数组martix,维护一个a数组作为二维前缀和,然后通过sunRegion函数的公式可以求出左上坐标(row1,col1)右下坐标(row......
  • CF494B Obsessive String KMP+前缀和优化DP
    给一份看起来字数比较少的题解?题意给一个字符串\(s\),和一个字符串\(t\)。在\(s\)中取出任意多个不重叠的子串(可以有子串没有被取出),使得每个子串都包含\(t\),求方案......
  • leetcode 208. Implement Trie (Prefix Tree) 实现 Trie (前缀树) (中等)
    一、题目大意Trie(发音类似"try")或者说前缀树是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。......
  • 最长公共前缀
    描述给你一个大小为n 的字符串数组strs,其中包含n个字符串,编写一个函数来查找字符串数组中的最长公共前缀,返回这个公共前缀。 数据范围: 0\len\le50000≤n......