题解:
这是一道LCT+期望。
先来看看这道题让求的E是啥。
我们可以发现,将树链压成序列后,第i项对总和产生的贡献为$i*(n-i+1)*ai$。
我们要维护这个东西。
考虑合并两个区间,那么先有$as[x]=as[ls]+as[rs]$;
然后考虑中间那个点,有$as[x]+=a[x]*(siz[ls]+1)*(siz[rs]+1)$;
然后考虑两边影响。
由于单点影响为$i*(n-i+1)*ai$,那么对于前边序列来说,$(n-i+1)$这一项增加了$siz[rs]+1$,
对于后边序列来说,$i$这一项增加了$siz[ls]+1$,
因此我们需要记录$sigma i*ai$,以及$sigma (n-i+1)*ai$,还有$sigma ai$。
转移很简单。
操作1,2为cut,link;
操作3为树链加。打个标记扔到转移里即可。
操作4询问,取出这一段树链的$as$作分子。
分母为$siz*(siz+1)*(siz+2)/6$,比较好推。(就是写出一个式子然后把sigma打开)
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 50050 #define ll long long inline int rd() { int f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();} return f*c; } int n,m; ll a[N]; struct LCT { int fa[N],ch[N][2]; ll siz[N],a1[N],a2[N],a3[N],as[N],tag[N]; bool res[N]; void reser(int x) { if(!x)return ; res[x]^=1; swap(ch[x][0],ch[x][1]); swap(a1[x],a2[x]); } ll sz(int x) { return siz[x]*(siz[x]+1)/2*(siz[x]+2)/3; } ll Cm(int x) { return siz[x]*(siz[x]+1)/2; } void add(int x,ll d) { if(!x)return ; tag[x]+= d; a[x] += d; a1[x] += Cm(x)*d; a2[x] += Cm(x)*d; a3[x] += siz[x]*d; as[x] += sz(x)*d; } bool isroot(int x) { return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x; } void update(int x) { int ls = ch[x][0],rs = ch[x][1]; siz[x] = siz[ls]+siz[rs]+1; a1[x] = a1[ls] + a1[rs] + (a[x]+a3[rs])*(siz[ls]+1); a2[x] = a2[rs] + a2[ls] + (a[x]+a3[ls])*(siz[rs]+1); a3[x] = a3[ls] + a3[rs] + a[x]; as[x] = as[ls] + as[rs] + a1[ls]*(siz[rs]+1) + a2[rs]*(siz[ls]+1) + a[x]*(siz[ls]+1)*(siz[rs]+1); } void pushdown(int x) { if(res[x]) { reser(ch[x][0]); reser(ch[x][1]); res[x]=0; } if(tag[x]) { add(ch[x][0],tag[x]); add(ch[x][1],tag[x]); tag[x]=0; } } int st[N],tl; void down(int x) { st[tl=1]=x; while(!isroot(x))x=fa[x],st[++tl]=x; while(tl)pushdown(st[tl]),tl--; } void rotate(int x) { int y = fa[x],z = fa[y],k = (ch[y][1]==x); if(!isroot(y))ch[z][ch[z][1]==y]=x; fa[x]=z; ch[y][k] = ch[x][!k],fa[ch[x][!k]] = y; ch[x][!k] = y,fa[y] = x; update(y),update(x); } void splay(int x) { down(x); while(!isroot(x)) { int y = fa[x],z = fa[y]; if(!isroot(y)) (ch[y][1]==x)^(ch[z][1]==y)?rotate(x):rotate(y); rotate(x); } } void access(int x) { int y = 0; while(x) { splay(x); ch[x][1]=y; update(x); y = x,x = fa[x]; } } void mtr(int x) { access(x); splay(x); reser(x); } int findrt(int x) { access(x); splay(x); while(ch[x][0])x=ch[x][0]; splay(x); return x; } void link(int x,int y) { mtr(x); fa[x]=y; } void cut(int x,int y) { mtr(x); access(y); splay(y); if(ch[y][0]==x) { ch[y][0]=fa[x]=0; update(y); } } void Add(int x,int y,ll d) { mtr(x); access(y); splay(y); add(y,d); } void init() { for(int i=1;i<=n;i++) { siz[i] = 1; a1[i] = a2[i] = a3[i] = as[i] = a[i]; } } }tr; ll gcd(ll x,ll y) { return y?gcd(y,x%y):x; } int main() { n = rd(),m = rd(); for(int i=1;i<=n;i++) a[i] = rd(); tr.init(); for(int f,t,i=1;i<n;i++) { f = rd(),t = rd(); tr.link(f,t); } int opt,x,y;ll d; for(int i=1;i<=m;i++) { opt = rd(); x = rd(),y = rd(); if(opt==1) { tr.cut(x,y); }else if(opt==2) { if(tr.findrt(x)!=tr.findrt(y)) { tr.link(x,y); } }else if(opt==3) { d = rd(); if(tr.findrt(x)==tr.findrt(y))tr.Add(x,y,d); }else { if(tr.findrt(x)==tr.findrt(y)) { tr.mtr(x); tr.access(y); tr.splay(y); ll zi = tr.as[y]; ll mu = tr.Cm(y); ll Gcd = gcd(zi,mu); printf("%lld/%lld ",zi/Gcd,mu/Gcd); }else { puts("-1"); } } } return 0; }