求和
题目链接:https://www.luogu.com.cn/problem/P4427
题意:
给定一颗树,m次询问,每次询问u-v之间每个节点的k次方之和
思路:
树上前缀和:res=ans[u][k]+ans[v][k]-ans[lca(u,v)][k]-ans[fa[lca(u,v)][0]][k]
其中ans[i][j]为i节点到根节点的节点k次方之和
所以用dfs倍增递推求lca的板子,dfs时顺带求出每个幂次下该节点到根节点的幂次方之和
ans[v][i]=(ans[u][i]+mi[i])%mod
其中u是v的父亲节点,这是i次方下的转移方程
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define pb push_back
#define endl "\n"
#define int long long
#define fi first
#define se second
//#pragma GCC optimize(3)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const ll llmax=LLONG_MAX;
const int maxn=3e5+5;
const int mod=998244353;
int ans[maxn][51];//根到u的k次方和
int mi[51];
int k,n,m;
int dep[maxn];//存u的深度
int fa[maxn][50];//存从u向上跳pow(2,i)的祖先节点
//流程:1.dfs创建st表
//2.利用st表求LCA
vector<int>e[maxn];
void dfs(int u,int father){
for(int i=1;i<=19;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(int v:e[u]){
if(v!=father){
dep[v]=dep[u]+1;
fa[v][0]=u;
for(int i=1;i<=50;i++){
mi[0]=1;
mi[i]=mi[i-1]*dep[v]%mod;
ans[v][i]=(ans[u][i]+mi[i])%mod;
}
dfs(v,u);
}
}
}
int lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
for(int i=19;i>=0;i--){
if(dep[fa[u][i]]>=dep[v]){
u=fa[u][i];
}
if(u==v)return v;
}
for(int i=19;i>=0;i--){
if(fa[u][i]!=fa[v][i]){
u=fa[u][i];v=fa[v][i];
}
}
return fa[u][0];
}
void solve(){
cin>>n;
for(int i=1;i<=n-1;i++){
int u,v;cin>>u>>v;
e[u].pb(v);e[v].pb(u);
}
cin>>m;
for(int i=2;i<=n;i++){
ans[i][0]=1;
}
dfs(1,0);
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v>>k;
int zuxian=lca(u,v);
int res=(ans[u][k]+ans[v][k]-ans[zuxian][k]-ans[fa[zuxian][0]][k]+2*mod)%mod;
cout<<res<<endl;
}
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0);
int T=1;
while(T--){
solve();
}
return 0;
}