一、题目

点此看题

二、解法

题目的提示已经足够明显了吧,肯定是要写一个 \(\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. Springboot如何启用文件上传功能

    网上的文章在写 "springboot文件上传" 时,都让你加上模版引擎,我只想说,我用不上,加模版引擎,你是觉得我脑子坏了,还是觉得我拿不动刀了. springboot如何启用文 ...

  2. JPG学习笔记2(附完整代码)

    #topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...

  3. vue watch All In One

    vue watch All In One var vm = new Vue({ data: { a: 1, b: 2, c: 3, d: 4, e: { f: { g: 5 } } }, watch: ...

  4. ossutilmac64

    ossutilmac64 ossutil是以命令行方式管理OSS数据的工具,提供方便.简洁.丰富的存储空间(Bucket)和文件(Object)管理命令,支持Windows.Linux. Mac平台. ...

  5. Bazinga means

    Bazinga means Bazinga https://www.dictionary.com/e/slang/bazinga/ refs xgqfrms 2012-2020 www.cnblogs ...

  6. ES-Next @Decorator All In One

    ES-Next @Decorator All In One @装饰器 import { logged } from "./logged.mjs"; class C { @logge ...

  7. javascript change array length methods

    javascript change array length methods Array 改变数组长度的方法 push, pop shift, unshift, splice, fill, 不改变数组 ...

  8. how to watch vuex state update

    how to watch vuex state update watch https://vuex.vuejs.org/api/#watch https://vuex.vuejs.org/guide/ ...

  9. React Hooks & react forwardRef hooks & useReducer

    React Hooks & react forwardref hooks & useReducer react how to call child component method i ...

  10. VSCode & Node.js & debugger & inspector

    VSCode & Node.js & debugger & inspector F5 ws 元信息 (UUID) ws://127.0.0.1:46912/efa91bda-1 ...