题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3091
建议可以先做一下线段树版:2752高速公路
一看有删边加边,就是LCT的题了,然后推一推数学式子。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int N=100005;
int n,m;
int rt,rev[N],fa[N],ls[N],rs[N],w[N];
LL lazy[N],sum[N],H[N],L[N],R[N],siz[N],a[N];
LL gcd(LL a,LL b)
{
if(b==0) return a;
return gcd(b,a%b);
}
void Pushup(int x)
{
siz[x]=siz[ls[x]]+siz[rs[x]]+1;
sum[x]=sum[ls[x]]+sum[rs[x]]+a[x];
H[x]=H[ls[x]]+H[rs[x]]+L[ls[x]]*(siz[rs[x]]+1)+R[rs[x]]*(siz[ls[x]]+1)+(siz[ls[x]]+1)*(siz[rs[x]]+1)*a[x];
L[x]=L[ls[x]]+L[rs[x]]+(siz[ls[x]]+1)*(sum[rs[x]]+a[x]);
R[x]=R[ls[x]]+R[rs[x]]+(siz[rs[x]]+1)*(sum[ls[x]]+a[x]);
}
void Prev(int x)
{
if(!x) return;
swap(ls[x],rs[x]);
swap(L[x],R[x]);
rev[x]^=1;
}
void Psum(int x,LL v)
{
if(!x) return;
a[x]+=v;
L[x]+=(siz[x]+1)*siz[x]/2*v;
R[x]+=(siz[x]+1)*siz[x]/2*v;
H[x]+=siz[x]*(siz[x]+1)*(siz[x]+2)/6*v;
sum[x]+=siz[x]*v;
lazy[x]+=v;
}
void Pushdown(int x)
{
if(rev[x]) Prev(ls[x]),Prev(rs[x]),rev[x]=0;
if(lazy[x]) Psum(ls[x],lazy[x]),Psum(rs[x],lazy[x]),lazy[x]=0;
}
bool Check(int x)
{
return x!=ls[fa[x]]&&x!=rs[fa[x]];
}
void Rotate(int x)
{
int y=fa[x],z=fa[y];
if(!Check(y))
{
if(ls[z]==y) ls[z]=x;
else rs[z]=x;
}
fa[x]=z;
fa[y]=x;
if(x==ls[y]) ls[y]=rs[x],fa[rs[x]]=y,rs[x]=y;
else rs[y]=ls[x],fa[ls[x]]=y,ls[x]=y;
Pushup(y);
}
void Splay(int x)
{
int len=0;
w[++len]=x;
for(int i=x;!Check(i);i=fa[i]) w[++len]=fa[i];
for(int i=len;i>=1;i--) Pushdown(w[i]);
while(!Check(x))
{
int y=fa[x],z=fa[y];
if(!Check(y))
{
if(ls[y]==x^ls[z]==y) Rotate(x);
else Rotate(y);
}
Rotate(x);
}
Pushup(x);
}
void Access(int x)
{
int k=0;
while(x)
{
Splay(x);
rs[x]=k;
Pushup(x);
k=x;
x=fa[x];
}
}
void Makeroot(int x)
{
Access(x);
Splay(x);
Prev(x);
}
int Find(int x)
{
Access(x);
Splay(x);
while(ls[x]) x=ls[x];
return x;
}
void Link(int x,int y)
{
if(Find(x)==Find(y)) return;
Makeroot(x);
fa[x]=y;
}
void Cut(int x,int y)
{
if(Find(x)!=Find(y)) return;
Makeroot(x);
Access(y);
Splay(x);
rs[x]=fa[y]=0;
Pushup(x);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
int op,x,y;
LL v;
for(LL i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
Link(x,y);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&op,&x,&y);
if(op==1) Cut(x,y);
else if(op==2) Link(x,y);
else if(op==3)
{
scanf("%lld",&v);
if(Find(x)!=Find(y)) continue;
Makeroot(x);
Access(y);
Splay(x);
Psum(x,v);
}
else
{
if(Find(x)!=Find(y))
{
puts("-1");
continue;
}
Makeroot(x);
Access(y);
Splay(x);
LL son=H[x],mom=siz[x]*(siz[x]+1)/2;
LL o=gcd(son,mom);
printf("%lld/%lld\n",son/o,mom/o);
}
}
}