P4427 [BJOI2018]求和(LCA+前缀和)

https://www.luogu.com.cn/problem/P4427


 

题目描述

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

输入格式

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

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

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

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

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

输出格式

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


思路:暴力统计50次,记录前缀和,然后类似树上两点距离,用lca来优化。lca用树链可求

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e5+1000;
typedef long long LL;
const LL mod=998244353;
LL ksm(LL a,LL k){LL res=1;while(k){if(k&1) res=res*a%mod;a=a*a%mod;k>>=1;}return res%mod;}
vector<LL>g[maxn];
LL dep[maxn],fa[maxn],siz[maxn],son[maxn];
LL top[maxn];
LL sum[maxn][51];
void predfs(LL u,LL father){
    siz[u]=1;fa[u]=father;
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i];
        if(v==father) continue;
        dep[v]=dep[u]+1;
        for(LL k=1;k<=50;k++)
        sum[v][k]=(sum[u][k]%mod+ksm(dep[v],k)%mod)%mod;
        predfs(v,u);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]){
            son[u]=v;
        }
    }
}
void dfs(LL u,LL topx){
    top[u]=topx;
    if(!son[u]) return;
    dfs(son[u],topx);
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i];
        if(v==fa[u]||v==son[u]) continue;
        dfs(v,v);
    }
}
LL lca(LL u,LL v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n;cin>>n;
  for(LL i=1;i<n;i++){
    LL u,v;cin>>u>>v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  dep[0]=-1;dep[1]=0;
  predfs(1,0);
  dfs(1,1);
  ///cout<<"fuck"<<endl;
  LL m;cin>>m;
  while(m--)
  {
      LL u,v,k;cin>>u>>v>>k;
      LL LCA=lca(u,v);
      cout<<( (sum[u][k]%mod+sum[v][k]%mod)%mod-(sum[LCA][k]%mod+sum[fa[LCA]][k]%mod)%mod+mod)%mod<<endl;
  }
return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值