城市旅行

题目描述

\(W\) 国地人物博,有 \(n\) 座城市组成,共 \(n-1\) 条双向道路连接其中的两座城市,且任意两座城市都可相互到达。
风景秀美的 \(w\) 国吸引了无数慕名而来的游客,根据游客对每座城市的打分,我们定义第 \(i\) 座城市的美丽度为 \(a_i\)。一次从城市 \(x\) 到城市 \(y\) 的旅行,所获得的的偷悦指数为从城市 \(x\) 到 城市 \(y\) 所有城市的美丽度之和(包括\(x\)\(y\))。我们记这个值为 \(H(x,y)\)
现在小A在城市 \(X\),Sharon在城市 \(Y\),他们想知道如果在城市 \(X\) 到城市 \(Y\) 之间的所有城市中任选两座城市 \(x\)\(y\) (\(x\)可以等于\(y\)),那么 \(H(x,y)\) 的期望值是多少,我们记这个期望值 \(E(x,y)\)
当然,城市之间的交通状况飘忽不定,因此我们不能排除某些时刻某些道路将无法通行。某些时刻会突然添加新的道路。以及游客们审美观的改变,某些城市的美丽度也会发生变化。作为 \(W\) 国负责旅游行业的T君,他要求你来写一个程序来模拟上而的所有过程。

 

输入格式

第一行两个整数,\(n,m\) 表示城市个数和操作个数。

接下来一行 \(n\) 个整数,第 \(i\) 个表示 \(a_i\) 。 接下来 \(n-1\) 行,每行两个整数 \(u,v\),表示 \(u\)\(v\) 之间有一条路。 接下来 \(m\) 行,是进行下面的操作:
1 u v 如果城市 \(u\) 和城市 \(v\) 已经无直接连接的道路,则忽略这个操作,否则删除 \(u,v\) 之间的道路。
2 u v 如果城市 \(u\) 和城市 \(v\) 联通那么忽略。否则在 \(u,v\) 之间添加一条道路。
3 u v d 如果城市 \(u\) 和城市 \(v\) 不连通,那么忽略。否则将城市 \(u\) 到城市 \(v\) 的路径中所有城市(包括\(u\)\(v\))的美丽度都增加d。

4 u v 询问 \(E(u,v)\) 的值

 

输出格式

对于操作4,输出答案,一个经过化简的分数 \(p/q\) 。如果 \(u\)\(v\) 不连通输出 \(-1\)

放在数学专题里的毒瘤数据结构题

首先发现这是个假期望,最后分母其实就是 \(\binom{len}{2}\)

然后前三个操作都是lct的基础操作,可以直接维护

现在我们考虑怎么得到一条路径上的所有答案和

先考虑如果在一个序列上 \([l,r]\) 的答案就是 \(\sum\limits_{i=l}^ra_i\times (i-l+1)\times (r-i+1)\)

转化到树上,对于询问 u v 考虑如果使 \(v\) 为树根,那么答案为 \(\sum\limits_{i\in u->v }a_i\times dep_i\times (dep_x-dep_i+1)\)

拆一下\(\sum\limits_{i\in u->v }a_i\times dep_i\times (dep_x-dep_i+1)=\sum\limits_{i\in u->v}a_i\times dep_i(dep_x+1)-a_i\times dep_i^2\)

于是我们需要维护 \(da[x]=\sum\limits_{i\in x} a_i\times dep_i\)\(da2[x]=\sum\limits_{i\in x} a_i\times dep_i^2\)

考虑如何合并两个子树的信息

首先左子树的答案不变可以直接加上,考虑右子树答案的变化

由于我们统计答案时一定是一条链,那么右子树每个点的 \(dep\) 都应该增加 \(sz[lc]+1\)

那么

\[\begin{aligned} da[x]&=da[lc]+(sz[lc]+1)\times a[x]+\sum\limits_{i\in rc}(sz[lc]+1+dep[i]')\times a[i]\\ &=da[lc]+da[rc]+(a[x]+sum[rc])\times (sz[lc]+1) \\ da2[x]&=da2[lc]+(sz[lc]+1)^2\times a[x]+\sum\limits_{i\in rc}(sz[lc]+1+dep[i]')^2\times a[i]\\ &=da2[lc]+(sz[lc]+1)^2\times a[x]+\sum\limits_{i\in rc}((sz[lc]+1)^2+2\times(sz[lc]+1)\times dep[i]'+dep[i]'^2)\times a[i]\\ &=da2[lc]+da2[rc]+(sum[rc]+a[x])\times (sz[lc]+1)^2+2(sz[lc]+1)\times \sum_{i\in rc}dep[i]'a[i]\\ &=da2[lc]+da2[rc]+(sum[rc]+a[x])\times (sz[lc]+1)^2+2\times da[rc]\times (sz[lc]+1) \end{aligned}\]

再考虑修改,考虑一条链所有点权值增加 \(d\) 造成的增量,有

\[\begin{aligned} \Delta da[x]&=\sum_{i\in x}dep[i]\times d\\ &=\frac{sz[x]\times(sz[x]+1)}{2}\times d\\ \Delta da2[x]&=\sum_{i\in x}dep[i]^2\times d\\ &=\frac{sz[x]\times(sz[x]+1)\times(sz[x]\times 2+1)}{6}\times d \end{aligned}\]

完了?不不不,我们发现 \(makeroot\) 的时候整棵树的深度会翻转,这样的话每个点的 \(dep\) 也会改变

但是我们发现改变只是 \(dep\) 从原来左儿子的 \(sz\) 变成了原来右儿子的 \(sz\)

那么我们只需要维护两套 \(da\)\(da2\),翻转的时候交换他们就行了

链加还需要维护懒标记

总共需要维护 \(lda,lda2,rda,rda2,sz,a,lazy\ \ 7\) 个变量

\(link\)\(cut\) 之前需要先下传标记(具体实现只要在操作之前 \(split\) 一下)

code

#include<bits/stdc++.h>
#define ll long long
#define cri const register int
#define re register
#define int ll
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
int c[50010][2],la[50010],l2a[50010],sz[50010],ra[50010],r2a[50010],lz[50010],a[50010];
int f[50010],r[50010],sta[50010],sum[50010];
inline bool nrt(cri x){ return c[f[x]][0]==x||c[f[x]][1]==x; }
inline void up(cri x){
	sz[x]=sz[lc]+sz[rc]+1;
	sum[x]=sum[lc]+sum[rc]+a[x];
	la[x]=(a[x]+sum[rc])*(sz[lc]+1)+la[lc]+la[rc];
	ra[x]=(a[x]+sum[lc])*(sz[rc]+1)+ra[lc]+ra[rc];
	l2a[x]=(a[x]+sum[rc])*(sz[lc]+1)*(sz[lc]+1)+l2a[lc]+l2a[rc]+la[rc]*(sz[lc]+1)*2;
	r2a[x]=(a[x]+sum[lc])*(sz[rc]+1)*(sz[rc]+1)+r2a[lc]+r2a[rc]+ra[lc]*(sz[rc]+1)*2;
}
inline void fz(cri x){
	r[x]^=1;
	swap(lc,rc);
	swap(la[x],ra[x]);swap(l2a[x],r2a[x]);
}
inline void add(cri x,cri y){
	lz[x]+=y; a[x]+=y;
	sum[x]+=sz[x]*y;
	la[x]+=sz[x]*(sz[x]+1)/2*y;
	ra[x]+=sz[x]*(sz[x]+1)/2*y;
	l2a[x]+=sz[x]*(sz[x]+1)*(sz[x]<<1|1)/6*y;
	r2a[x]+=sz[x]*(sz[x]+1)*(sz[x]<<1|1)/6*y;
}
inline void down(cri x){
	if(lc){
		add(lc,lz[x]);
		if(r[x]) fz(lc);
	}
	if(rc){
		add(rc,lz[x]);	
		if(r[x]) fz(rc);
	}
	r[x]=lz[x]=0;
}
inline void rotate(cri x){
	cri y=f[x],z=f[y],k=c[y][1]==x,w=c[x][k^1];
	if(nrt(y)) c[z][c[z][1]==y]=x;
	c[x][k^1]=y,c[y][k]=w;
	if(w) f[w]=y; f[y]=x; f[x]=z;
	up(y);up(x);
}
inline void splay(int x){
	int z=0,y=x;
	sta[++z]=y;
	while(nrt(y)) sta[++z]=y=f[y];
	while(z) down(sta[z--]);
	while(nrt(x)){
		y=f[x],z=f[y];
		if(nrt(y)) rotate(c[z][1]==y^c[y][1]==x?x:y);
		rotate(x);
	}
}
inline void ac(int x){
	for(int y=0;x;x=f[y=x]) splay(x),rc=y,up(x);
}
inline int mrt(cri x){
	ac(x);splay(x);fz(x);
}
inline int frt(int x){
	ac(x);splay(x);
	while(lc) down(x),x=lc;
	splay(x);
	return x;
}
inline void split(cri x,cri y){
	mrt(x),ac(y),splay(y);
}
inline void link(cri x,cri y){
	if(frt(x)==frt(y)) return;
	split(x,y);f[x]=y;
}
inline void cut(cri x,cri y){
	split(x,y);
	if(c[y][0]!=x||c[x][1]) return;
	f[x]=c[y][0]=0;
	up(y);
}
signed main(){
	int n,m;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),up(i);
	for(int i=1,x,y;i<n;i++) scanf("%lld%lld",&x,&y),link(x,y);
	while(m--){
		int opt,u,x,d;
		scanf("%lld%lld%lld",&opt,&u,&x);
		if(opt==1) cut(u,x);
		if(opt==2) link(u,x);
		if(opt==3){
			scanf("%lld",&d);
			if(frt(u)!=frt(x)) continue;
			split(u,x);
			add(x,d);
		}
		if(opt==4){
			if(frt(u)!=frt(x)) puts("-1");
			else{
				split(u,x);
				int ans=la[x]*(sz[x]+1)-l2a[x],base=sz[x]*(sz[x]+1)/2;
				printf("%lld/%lld\n",ans/__gcd(ans,base),base/__gcd(ans,base));
			}
		}
	}
}
posted @ 2020-01-03 19:02  mikufun♘  阅读(158)  评论(0编辑  收藏  举报