题目描述
(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)。
放在数学专题里的毒瘤数据结构题
首先发现这是个假期望,最后分母其实就是 (inom{len}{2})
然后前三个操作都是lct的基础操作,可以直接维护
现在我们考虑怎么得到一条路径上的所有答案和
先考虑如果在一个序列上 ([l,r]) 的答案就是 (sumlimits_{i=l}^ra_i imes (i-l+1) imes (r-i+1))
转化到树上,对于询问 u v
考虑如果使 (v) 为树根,那么答案为 (sumlimits_{iin u->v }a_i imes dep_i imes (dep_x-dep_i+1))
拆一下(sumlimits_{iin u->v }a_i imes dep_i imes (dep_x-dep_i+1)=sumlimits_{iin u->v}a_i imes dep_i(dep_x+1)-a_i imes dep_i^2)
于是我们需要维护 (da[x]=sumlimits_{iin x} a_i imes dep_i) 和 (da2[x]=sumlimits_{iin x} a_i imes dep_i^2)
考虑如何合并两个子树的信息
首先左子树的答案不变可以直接加上,考虑右子树答案的变化
由于我们统计答案时一定是一条链,那么右子树每个点的 (dep) 都应该增加 (sz[lc]+1)
那么
再考虑修改,考虑一条链所有点权值增加 (d) 造成的增量,有
完了?不不不,我们发现 (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
",ans/__gcd(ans,base),base/__gcd(ans,base));
}
}
}
}