5911. 【NOIP2018模拟10.18】Travel

题目大意:

EZ同学家里非常富有,但又极其的谦虚,说话又好听,是个不可多得的人才。
EZ常常在假期环游世界,他准备去N(N<=100000)个国家之多,一些国家有航线连接,由于EZ同学有一定的强迫症,任意两个国家之间都能通过航路直接或间接到达,并且这样的路径仅有一种。(简单来说,这些国家构成了一棵树)
由于EZ是C国人,因此将C国(1号国家)作为整棵树的根
每个国家有一个旅游热度A[i]和影响力D[i]。由于目的地有点多,为了避免选择困难症,他给每个国家设置了一个向往值F[i],它等于所有的A[j]之和,满足i国在j国向C国走D[j]步的路径上(经过一条航路算一步,i=j也会被统计,如果D[j]步超过了C国,则超出部分不用管)。
LYD同学家里有矿,富有程度与EZ不相上下,但他却在宅与现充间摇摆不定。某次机缘巧合,EZ外出旅游刺激了LYD,他决定也要开始旅游。为了避免又被判高重复率导致被取消资格,他将EZ的旅游地图略微做了一点调整,每条航路将有一定的概率出现。
现在他有Q个询问,每次询问某个国家所在的联通块(由于每条边是一定概率出现,因此它所在的联通块可以是很多种)中所有国家的F[i]值的和的平方的期望(对998244353取模),以此来决定他旅游的目的地。但他极其厌恶繁琐的计算,于是他找到了能算出圆周率并将它倒背下来的你,答应给你丰厚的报酬。家里没矿,老爸也不是X达集团老总的你决定接受他的任务。

思路:

先考虑f怎么求,我们可以在每个点X打上+的标记,在d[i]+1祖宗打上-的标记。然后dfs一次就可以求出来f了,不需要树剖。
然后考虑则维护合的平方的希望。因为 ( a + b ) 2 = a 2 + 2 ∗ a ∗ b + b 2 (a+b)^2=a^2+2*a*b+b^2 (a+b)2=a2+2ab+b2,所以我们要记录每个子树的和和每个子树平方和。然后合并的时候就很简单了,这样一次dfs就可以求出f[1]的答案了。
然后怎么求其他点的答案呢,我们对于一个一个点只要知道他父亲过来平方和,和和,然后用前缀和优化一下每个兄弟的转移就好了。

程序:

#include
#include
#include
#include
#define LL long long
using namespace std;
const LL N=200005;
const LL mo=998244353;
LL fa[N][21],vd[N],fg[N],fh[N],f[N],g[N],ans[N],last[N],a[N],d[N],t[N],love[N];
LL n,sb,cnt;
struct tree{LL to,next,w;}e[N*2];
LL sqr(LL x){
	return (x*x)%mo;
}

void add(LL x,LL y,LL w){
	e[++cnt].to=y; e[cnt].next=last[x]; e[cnt].w=w; last[x]=cnt;
	e[++cnt].to=x; e[cnt].next=last[y]; e[cnt].w=w; last[y]=cnt;
}

LL dfs(LL x,LL father){
	fa[x][0]=father;
	LL o=0;
	for (LL i=last[x];i;i=e[i].next)
	if (e[i].to!=father) o=(o+dfs(e[i].to,x))%mo;
	f[x]=(t[x]+o)%mo;
	g[x]=sqr(f[x]);
	love[x]=f[x];
	for (LL i=last[x];i;i=e[i].next)
	if (e[i].to!=father){
		g[x]=(g[x]+e[i].w*(g[e[i].to]+(2*f[e[i].to])%mo*f[x]%mo))%mo;	
		f[x]=(f[x]+e[i].w*f[e[i].to])%mo;
	}
	return t[x]+o;
}

void dfs1(LL x,LL fa,LL fn){
	ans[x]=(g[x]+fn*(fg[x]+2*fh[x]*f[x]%mo)%mo)%mo;
	LL g1=(sqr(love[x])+fn*(fg[x]+2*fh[x]*love[x]%mo))%mo;
	LL h1=(fh[x]*fn+love[x])%mo;
	d[0]=0;
	for (LL i=last[x];i;i=e[i].next){
		LL p=e[i].to;
		if (p!=fa){
			fg[p]=g1,fh[p]=h1;
			g1=(g1+e[i].w*(g[p]+2*h1*f[p]%mo)%mo)%mo;
			h1=(h1+e[i].w*f[p])%mo;
			d[++d[0]]=p;
			vd[d[0]]=e[i].w;
		}
	}
	g1=0,h1=0;
	for (LL i=d[0];i>=1;i--){
		LL p=d[i];
		fg[p]=(fg[p]+g1+2*fh[p]*h1%mo)%mo;
		fh[p]=(fh[p]+h1)%mo;
		g1=(g1+vd[i]*(g[p]+2*h1*f[p]%mo)%mo)%mo;
		h1=(h1+vd[i]*f[p]%mo)%mo;
	}
	for (LL i=last[x];i;i=e[i].next)
	if (e[i].to!=fa) dfs1(e[i].to,x,e[i].w);
}

LL lca(LL x,LL s){
	LL i=20;
	while (i>=0){
		if ((1<<i)<=s) x=fa[x][i],s-=(1<<i);
		i--;
	}
	return x;
}

void shen(int x,int father){
	fa[x][0]=father;
	for (int i=last[x];i;i=e[i].next)
	if (e[i].to!=father) shen(e[i].to,x);
}

int main(){
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	scanf("%lld",&n);
	for (LL i=1;i<=n;i++)	scanf("%lld%lld",&a[i],&d[i]);
	for (LL i=1;i<n;i++){
		LL x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		add(x,y,z);
	}
	shen(1,0);
	for (LL i=1;i<=20;i++)
	 for (LL j=1;j<=n;j++)
	  fa[j][i]=fa[fa[j][i-1]][i-1];
	for (LL i=1;i<=n;i++){
		t[i]=(t[i]+a[i])%mo;
		LL u=lca(i,d[i]+1);
		t[u]=(t[u]-a[i]+mo)%mo;
	}
	dfs(1,0);
	dfs1(1,0,0);
	scanf("%lld",&sb);
	for (LL i=1;i<=sb;i++){
		LL x;
		scanf("%lld",&x);
		printf("%lld\n",ans[x]);
	}
}

你可能感兴趣的:(树,lca,何嘉阳)