求和

题目链接: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;
}


posted @ 2025-03-15 18:14  Marinaco  阅读(7)  评论(0)    收藏  举报
//雪花飘落效果