一、题目

点此看题

二、解法

题目的提示已经足够明显了吧,肯定是要写一个 \(\tt link-cut-tree\) 。我们只需要求出总和,再除以方案数就是期望。然后可以算每个点为 \(\tt lca\) 时的贡献。

但是要对子树搞点事情,而子树分为虚子树和实子树(看连接的是什么边),所以两类都要维护,实子树可以用 \(\tt push\_up\) 操作维护,虚子树要在变动父子关系的时候维护,有点麻烦。要维护下列信息:

  • \(x\) 的虚子树大小(包括 \(x\) 这个点):\(siz\)
  • \(x\) 的子树中节点总个数:\(sum\)
  • \(x\) 节点包括其子树中的每个点的答案和:\(ans\)
  • \(x\) 子树内所有点的 \(siz\times a\) :\(all\)
  • \(x\) 所有虚子树的答案:\(ad\)
  • \(x\) 所有虚子树 \(siz\) 的平方求和:\(de\)

虚子树的信息你肯定是会维护的,\(sum\) 和 \(ans\) 你肯定也会,我就来详细讲一讲 \(ans\) 怎么算,要分成四部分:

  • 拿到其子树的答案:\(ans[ls]+ans[rs]+ad[x]\)
  • 虚子树之间的贡献,也就是在 \(siz[x]\) 中乱选两个点,再把两点选在同一个子树中的方案给删掉:\((siz[x]\times siz[x]-de[x])\times a[x]\)
  • 虚子树和实子树(\(x\) 的右儿子)之间的贡献,它们的 \(\tt lca\) 是 \(x\):\(2\times a[x]\times siz[x]\times sum[ch[x][1]]\)
  • \(x\) 的子树和 \(x\) 祖先的贡献(\(x\) 的左儿子),这个贡献由于 \(\tt splay\) 结构的原因没有被统计到,反正是求总和,我们把这个贡献放在 \(x\) 这里也没关系:\(2\times all[ch[x][0]]\times (sum[x]-sum[ch[x][0]])\)

然后因为要保证是有根树所以不能用 \(\tt makeroot\) 。那么 \(\tt link(x,y)\) 就把 \(x,y\) 都转到根然后连虚边,\(\tt cut\) 就把 \(x\) 转到根,\(y\) 转到 \(x\) 下面然后删虚边。在 \(\tt access,cut,link\) 的时候都要改虚子树的信息哦。

反正写的时候就是非常爽,非常爽。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 50005;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,a[M],siz[M],sum[M],ans[M];char s[10];
int fa[M],all[M],ad[M],de[M],ch[M][2],par[M];double r;
/*
siz[x]表示x的虚子树大小(包括x)
sum[x]表示x为根的子树节点个数
ans[x]表示x的答案
all[x]表示x子树内的siz[y]*a[y]
ad[x]表示x虚子树的答案
de[x]表示x的虚子树的sum的平方的和
*/
void up(int x)
{
sum[x]=siz[x]+sum[ch[x][0]]+sum[ch[x][1]];
all[x]=all[ch[x][0]]+all[ch[x][1]]+a[x]*siz[x];
ans[x]=ans[ch[x][0]]+ans[ch[x][1]]+ad[x]//第一部分,直接累加
+a[x]*(siz[x]*siz[x]-de[x])//第二部分,虚子树的贡献
+2*a[x]*sum[ch[x][1]]*siz[x]//第三部分,虚实之间的贡献,lca是x
+2*all[ch[x][0]]*(sum[x]-sum[ch[x][0]]);//第四部分,把祖先的答案算到它上面
}
int nrt(int x)//判断是不是实边
{
return ch[par[x]][0]==x || ch[par[x]][1]==x;
}
int chk(int x)//判断是哪个儿子
{
return ch[par[x]][1]==x;
}
void rotate(int x)
{
int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];
ch[y][k]=w;par[w]=y;
if(nrt(y)) ch[z][chk(y)]=x;par[x]=z;
ch[x][k^1]=y;par[y]=x;
up(y);up(x);
}
void splay(int x)//转到实链的最上面
{
while(nrt(x))
{
int y=par[x];
if(nrt(y))
{
if(chk(x)==chk(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)
{
for(int y=0;x;x=par[y=x])
{
splay(x);
//先把ch[x][1]加进虚子树中
siz[x]+=sum[ch[x][1]];
ad[x]+=ans[ch[x][1]];
de[x]+=sum[ch[x][1]]*sum[ch[x][1]];
//再把y从虚子树中拿出来
siz[x]-=sum[y];
ad[x]-=ans[y];
de[x]-=sum[y]*sum[y];
ch[x][1]=y;up(x);
}
}
void link(int x,int y)//把(x,y)连一条边,x是祖先
{
access(y);
splay(y);
access(x);
splay(x);
par[y]=x;
siz[x]+=sum[y];
ad[x]+=ans[y];
de[x]+=sum[y]*sum[y];
up(x);
}
void cut(int x,int y)//把(x,y)这条边去掉,x是祖先
{
access(x);
splay(x);
splay(y);
par[y]=0;
siz[x]-=sum[y];
ad[x]-=ans[y];
de[x]-=sum[y]*sum[y];
up(x);
}
int check(int x,int y)//判断x是不是y的祖先
{
access(y);
splay(y);
splay(x);
return nrt(y);
}
signed main()
{
n=read();
for(int i=2;i<=n;i++)
fa[i]=read();
for(int i=1;i<=n;i++)
{
a[i]=ans[i]=all[i]=read();
siz[i]=sum[i]=1;
}
for(int i=2;i<=n;i++)
link(fa[i],i);
m=read();
access(1);
splay(1);
r=ans[1];
printf("%.10lf\n",r/n/n);
while(m--)
{
scanf("%s",s);
int x=read(),y=read();double r=0;
if(s[0]=='P')
{
if(check(x,y)) swap(x,y);
cut(fa[x],x);
fa[x]=y;
link(fa[x],x);
access(1);
splay(1);
r=ans[1];
}
else
{
access(x);
splay(x);
a[x]=y;
up(x);
r=ans[x];
}
printf("%.10lf\n",r/n/n);
}
}

CF482E ELCA的更多相关文章

  1. 【CF 482E】ELCA

    题意 题解 50pts 由于这题 \(2s\),所以可以信仰一波,暴力修改.查询. 暴力修改的复杂度是 \(O(n)\),暴力查询的复杂度是 \(O(n^2)\). 但不难发现可以通过记录子树大小来优 ...

  2. Codeforces 482E ELCA (LCT)

    题目链接 http://codeforces.com/contest/482/problem/E 题解 T2智商题T3大LCT题,我一个也不会= = CF的标算好像是分块?反正现在LCT都普及了就用L ...

  3. CF数据结构练习

    1. CF 438D The Child and Sequence 大意: n元素序列, m个操作: 1,询问区间和. 2,区间对m取模. 3,单点修改 维护最大值, 取模时暴力对所有>m的数取 ...

  4. CORBA简介

    使用.NET开发corba应用 一. 什么是IIOP.NET IIOP.NET 是通过使用基于corba的IIOP支持.NET.javaEE和corba组件实现无缝互操作的技术.如图1.1所示,这种解 ...

随机推荐

  1. docker-理论题01

    1.什么是docker?答:docker是开源的应用容器引擎:开发人员把他们的应用及依赖包打包发布到容器当中. 2.docker和VMware的区别? 答:docker是半解耦,VMware是解耦:d ...

  2. 宝塔面板&FLASK&centos 7.2 &腾讯云 配置网站出现的若干问题

    1.解决跨域问题&&中文显示问题 from flask import Flask, url_for, request, render_template, redirect from f ...

  3. tfrecords转图片存储

    import os import shutil import tensorflow as tf import time import sys import cv2 # 图片存放位置 PATH_RES ...

  4. vuepress & package.json lock version

    vuepress & package.json lock version npm 锁版 bug npm lock version holy shit { "name": & ...

  5. HTML5 Canvas 画图组件 All In One

    HTML5 Canvas 画图组件 All In One Canvas SVG Canvas + SVG refs https://www.infoq.cn/article/1BVg9VDSmqyHv ...

  6. jQuery 使用注意事项 与 小技巧(tips)

    jQuery 使用注意事项 与 小技巧(tips) 1 $( document ).ready() https://learn.jquery.com/using-jquery-core/documen ...

  7. what's the print number means after called the setTimeout function in Chrome console?

    what's the print number means after called the setTimeout function in Chrome console? javascript fun ...

  8. NGK每日快讯2021.1.29日NGK公链第87期官方快讯!

  9. 详解稳定币圣杯USDN

    稳定币飞速发展,USDN 一骑绝尘,但因合规问题饱受质疑.合规稳定币作为后来者,奋起直追,亦光耀夺目.而更符合区块链精神的稳定币(抵押其他资产生成稳定币),长期以来只有 Maker 的 DAI 能够在 ...

  10. NGK全球启动大会圆满召开,一起预见区块链的美好未来!

    NGK Global全球启动大会在高新技术产业区硅谷于美国当地时间11月25日圆满召开!这次会议邀请了星盟高管.NGK全球各大市场领导人.NGK生态产业代表等上百位嘉宾出席,此次会议持续了一个多小时, ...