维护 $sum_{}v[i] imes dep[i]$ 与 $sum_{} v[i] imes dep[i]^2$
其中 $dep[i]$ 表示 $i$ 点在该点所在 $LCT$ 的 $splay$ 中的中序遍历序.
然后正着维护一个,反着维护一个就行了.
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <algorithm>
#define N 50005
#define ll long long
#define siz size
#define ts tag
#define lson s[x].ch[0]
#define rson s[x].ch[1]
#define get(x) (s[s[x].f].ch[1]==x)
#define Isr(x) (!(s[s[x].f].ch[0]==x||s[s[x].f].ch[1]==x))
using namespace std;
ll sqr(ll x) { return x*x; }
ll calc1(ll x) { return x*(x+1ll)/2; }
ll calc2(ll x) { return x*(x+1ll)*(2ll*x+1ll)/6; }
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; }
namespace IO {
void setIO(string s)
{
string in=s+".in";
string out=s+".out";
freopen(in.c_str(),"r",stdin);
freopen(out.c_str(),"w",stdout);
}
};
int sta[N];
map<int,int>con[N];
struct node {
int ch[2],rev,f;
ll s0[2],s1[2],s2[2],tag,v,size;
}s[N];
void Pushup(int x)
{
s[x].size=s[lson].size+s[rson].size+1;
s[x].s0[0]=s[lson].s0[0]+s[rson].s0[0]+s[x].v;
s[x].s1[0]=s[lson].s1[0]+s[rson].s1[0]+(s[rson].s0[0]+s[x].v)*(s[lson].size+1);
s[x].s2[0]=s[s[x].ch[0]].s2[0]+s[s[x].ch[1]].s2[0]+(s[s[x].ch[1]].s0[0]+s[x].v)*(s[s[x].ch[0]].size+1)*(s[s[x].ch[0]].size+1)+2ll*s[s[x].ch[1]].s1[0]*(s[s[x].ch[0]].size+1);
s[x].s0[1]=s[lson].s0[1]+s[rson].s0[1]+s[x].v;
s[x].s1[1]=s[rson].s1[1]+s[lson].s1[1]+(s[lson].s0[1]+s[x].v)*(s[rson].size+1);
s[x].s2[1]=s[s[x].ch[1]].s2[1]+s[s[x].ch[0]].s2[1]+(s[s[x].ch[0]].s0[1]+s[x].v)*(s[s[x].ch[1]].size+1)*(s[s[x].ch[1]].size+1)+2ll*s[s[x].ch[0]].s1[1]*(s[s[x].ch[1]].size+1);
}
void Up_tag(int x,ll v)
{
s[x].v+=v;
s[x].tag+=v;
s[x].s0[0]+=s[x].size*v;
s[x].s0[1]+=s[x].size*v;
s[x].s1[0]+=calc1(s[x].size)*v;
s[x].s1[1]+=calc1(s[x].size)*v;
s[x].s2[0]+=calc2(s[x].size)*v;
s[x].s2[1]+=calc2(s[x].size)*v;
}
void Up_rev(int x)
{
s[x].rev^=1;
swap(lson,rson);
swap(s[x].s0[0],s[x].s0[1]);
swap(s[x].s1[0],s[x].s1[1]),swap(s[x].s2[0],s[x].s2[1]);
}
void Pushdown(int x)
{
if(s[x].tag)
{
if(lson) Up_tag(lson,s[x].tag);
if(rson) Up_tag(rson,s[x].tag);
s[x].tag=0;
}
if(s[x].rev)
{
if(lson) Up_rev(lson);
if(rson) Up_rev(rson);
s[x].rev=0;
}
}
void rotate(int x)
{
int old=s[x].f,fold=s[old].f,which=get(x);
if(!Isr(old)) s[fold].ch[s[fold].ch[1]==old]=x;
s[old].ch[which]=s[x].ch[which^1];
if(s[old].ch[which]) s[s[old].ch[which]].f=old;
s[x].ch[which^1]=old,s[old].f=x,s[x].f=fold;
Pushup(old),Pushup(x);
}
void Splay(int x)
{
int v=0,u=x,fa;
for(sta[++v]=u;!Isr(u);u=s[u].f) sta[++v]=s[u].f;
for(;v;--v) Pushdown(sta[v]);
for(u=s[u].f;(fa=s[x].f)!=u;rotate(x))
{
if(s[fa].f!=u)
rotate(get(fa)==get(x)?fa:x);
}
}
void Access(int x)
{
for(int y=0;x;y=x,x=s[x].f)
{
Splay(x);
rson=y;
Pushup(x);
}
}
void Make_Root(int x)
{
Access(x);
Splay(x);
Up_rev(x);
}
void Link_Edge(int x,int y)
{
Access(x),Splay(x);
Make_Root(y);
s[y].f=x;
}
void Cut_Edge(int x,int y)
{
Make_Root(x),Access(y),Splay(y);
if(!s[x].ch[1]&&s[y].ch[0]==x) s[y].ch[0]=s[x].f=0;
Pushup(y);
}
void Split(int x,int y)
{
Make_Root(x),Access(y),Splay(y);
}
int Find_Root(int x)
{
while(s[x].f) x=s[x].f;
return x;
}
int main()
{
// IO::setIO("input");
int i,j,n,m;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i) scanf("%lld",&s[i].v),Pushup(i);
for(i=1;i<n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
Link_Edge(x,y);
con[x][y]=con[y][x]=1;
}
for(i=1;i<=m;++i)
{
int op,x,y,z;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&x,&y);
if(!con[x][y]) continue;
else
{
Cut_Edge(x,y);
con[x][y]=con[y][x]=0;
}
}
if(op==2)
{
scanf("%d%d",&x,&y);
if(Find_Root(x)==Find_Root(y)) continue;
else
{
con[x][y]=con[y][x]=1;
Link_Edge(x,y);
}
}
if(op==3)
{
scanf("%d%d%d",&x,&y,&z);
if(Find_Root(x)!=Find_Root(y)) continue;
Split(x,y);
Up_tag(y,(ll)z);
}
if(op==4)
{
scanf("%d%d",&x,&y);
if(Find_Root(x)!=Find_Root(y)) printf("-1
");
else
{
Split(x,y);
ll dn=s[y].size*(s[y].size+1)/2;
ll up=(s[y].size+1ll)*s[y].s1[0]-s[y].s2[0];
ll g=gcd(up,dn);
printf("%lld/%lld
",up/g,dn/g);
}
}
}
return 0;
}