【JZOJ5911】Travel

本文探讨了一种基于树形结构的动态规划方法,用于解决概率性查询问题。通过树上的差分技巧,子树求和及连通块概率计算,实现对节点所在联通块所有特定函数值平方和的期望值的高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

有一棵以1为根的树,每个点有aia_iaidid_idi,定义FiF_iFi为所有的aja_jaj的和,满足jjj往根走djd_jdj步的路径上有iii。每条边有一定概率出现(对FFF无影响),Q次询问,每次询问xxx点所在的联通块所有FiF_iFi和的平方的期望。

Solution

首先求FiF_iFi,可以再树上查分,子树求和。
连通块很难处理,考虑一个点与它子树一部分组成连通块的期望,和很好处理,考虑和的平方,设当前块为aaa,要加入的子树块为bbb ,则新的块c=p(a+b)2+(1−p)a2c=p(a+b)^2+(1-p)a^2c=p(a+b)2+(1p)a2ppp为连接两个块的边的出现概率)
于是拆式子:c=a2+p(2ab+b2)c=a^2+p(2ab+b^2)c=a2+p(2ab+b2),然后直接转移,根节点的答案即为询问该点的期望。
这题有多组询问,我们可以对111做完dpdpdp后,从上至下换根即可。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
using namespace std;
typedef long long ll;
const int N=2e5+10,M=4e5+10,mo=998244353;
int a[N],d[N];
int to[M],nx[M],ls[N],vl[M],num=0;
ll F[N];
ll h[N],g[N];
void link(int u,int v,int w){
	to[++num]=v,nx[num]=ls[u],ls[u]=num;
	vl[num]=w;
}
int st[N],top=0;
void pre(int x,int fr){
	int fd=st[max(top-d[x],0)];
	st[++top]=x,F[x]+=a[x],F[fd]-=a[x];
	rep(i,x){
		int v=to[i];
		if(v==fr) continue;
		pre(v,x);
		F[x]+=F[v];
	}
	F[x]=(F[x]%mo+mo)%mo,st[top--]=0;
}
void dfs(int x,int fr){
	g[x]=F[x],h[x]=F[x]*F[x]%mo;
	rep(i,x){
		int v=to[i];
		ll p=vl[i];
		if(v==fr) continue;
		dfs(v,x);
		h[x]=(h[x]+(g[x]*g[v]*2+h[v])%mo*p%mo)%mo;
		g[x]=(g[x]+g[v]*p)%mo;
	}
}
ll G[N],H[N];
void calc(int x,int fr){
	rep(i,x){
		int v=to[i];
		ll p=vl[i];
		if(v==fr) continue;
		ll gv=(G[x]-g[v]*p%mo+mo)%mo,hv=(H[x]-(gv*g[v]%mo*2%mo+h[v])%mo*p%mo+mo)%mo;
		H[v]=(h[v]+(g[v]*gv%mo*2+hv)%mo*p%mo)%mo;
		G[v]=(g[v]+gv*p)%mo;
		calc(v,x);
	}
}
int main()
{
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	int n;
	scanf("%d",&n);
	fo(i,1,n) scanf("%d %d",&a[i],&d[i]);
	fo(i,2,n){
		int u,v,w;
		scanf("%d %d %d",&u,&v,&w);
		link(u,v,w),link(v,u,w);
	}
	pre(1,0);
	dfs(1,0);
	G[1]=g[1],H[1]=h[1];
	calc(1,0);
	int q;
	scanf("%d",&q);
	while(q--){
		int x;
		scanf("%d",&x);
		printf("%lld\n",H[x]);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值