Codeforces 482E ELCA (LCT)
题目链接
http://codeforces.com/contest/482/problem/E
题解
T2智商题T3大LCT题,我一个也不会= =
CF的标算好像是分块?反正现在LCT都普及了就用LCT好了。
首先算期望推个式子,易得答案为\(\sum_u a[u](sz[u]^2-\sum_{v\in son[u]} sz[v]^2)\) (\(sz\)为子树大小),令求和的那个东西等于\(f[u]\)
并且如果往一个\(u\)里新添一个儿子\(v\),增添后的子树大小是\(sz[v]\), 那么新增的答案是\(2a[u]sz[v](sz[u]-sz[v])\)
然后我们要支持换父亲,动态维护这个东西
后面的就是莽上一个LCT。
这个只能详见代码,解释一下代码里变量的含义
对于一个Splay结构体\(u\):
\(ans\): 这个点Splay的子树中所有点的\(f\)之和。Splay的根的\(ans\)就是要求的答案。
\(sz01\): 这个点所有虚子树的大小之和加上本身的\(1\)。(\(sz\)是原树中子树大小)
\(sz02\): 这个点本身所有虚子树的大小平方和,不包括本身。
\(ans0\): 这个点本身所有虚儿子的\(ans\)之和。
\(sz11\): 这个点所有实儿子和虚儿子的大小之和。
\(sum\): 这个点及其所有实儿子\(v\)的\(sz01[v]\times a[v]\)之和。
这些变量看起来有些匪夷所思,那么我们看下怎么维护。
首先,所有对虚儿子求和的变量(也就是名字里带0的三个)都在access时处理,pushup时无需处理。
现在考虑pushup那个函数,前两行处理的是\(sz11\)和\(sum\), 这个根据定义求就行,也没有什么好说的。
后四行是重点——\(ans\)的更新。
我们把\(ans[u]\)分了四部分统计。(选两个点\(x\)和\(y\)求\(a[lca(x,y)]\)之和)
第一部分: 两个点都选在\(u\)的祖先(splay左子树内),或都选在splay右子树内,或都选在\(u\)的同一棵虚子树内。
spl[u].ans = spl[ls].ans+spl[u].ans0+spl[rs].ans;
第二部分: 两个点选在\(u\)的两棵不同虚子树内。
spl[u].ans += (spl[u].sz01*spl[u].sz01-spl[u].sz02)*a[u];
第三部分: 两个点之一选在\(u\)的虚子树内,另一个点选在\(u\)的splay右子树内。这样LCA一定是\(u\).
spl[u].ans += 2ll*spl[u].sz01*spl[rs].sz11*a[u];
第四部分: 两个点之一选在\(u\)的祖先(splay左子树)或其虚子树内,另一个选在\(u\)的整个splay子树及子树内所有点的虚子树去掉\(u\)上方(splay左子树内及其虚子树)的部分,也就是\(u\)及其下方(splay右子树内)所有点及其虚子树内。
spl[u].ans += 2ll*spl[ls].sum*(spl[u].sz11-spl[ls].sz11);
于是就做完了。(第四部分确实有点复杂)
最后膜拜一发考场切此题的新初三巨佬
zjr nb!
代码
#include<cstdio>
#include<cstdlib>
#include<iostream>
#define llong long long
using namespace std;
void read(int &x)
{
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
const int N = 1e5;
struct SplayNode
{
int son[2],fa; llong sz01,sz11,sz02,sum,ans0,ans;
} spl[N+3];
int fa[N+3];
llong a[N+3];
int n,q;
bool isroot(int u) {return spl[spl[u].fa].son[0]!=u && spl[spl[u].fa].son[1]!=u;}
void pushup(int u)
{
int ls = spl[u].son[0],rs = spl[u].son[1];
spl[u].sz11 = spl[ls].sz11+spl[u].sz01+spl[rs].sz11;
spl[u].sum = spl[ls].sum+spl[rs].sum+spl[u].sz01*a[u];
spl[u].ans = spl[ls].ans+spl[u].ans0+spl[rs].ans;
spl[u].ans += (spl[u].sz01*spl[u].sz01-spl[u].sz02)*a[u];
spl[u].ans += 2ll*spl[u].sz01*spl[rs].sz11*a[u];
spl[u].ans += 2ll*spl[ls].sum*(spl[u].sz11-spl[ls].sz11);
}
void rotate(int u)
{
int x = spl[u].fa,y = spl[x].fa;
spl[u].fa = y;
if(!isroot(x)) {spl[y].son[x==spl[y].son[1]] = u;}
int dir = u==spl[x].son[0];
spl[x].son[dir^1] = spl[u].son[dir];
if(spl[u].son[dir]) {spl[spl[u].son[dir]].fa = x;}
spl[u].son[dir] = x; spl[x].fa = u;
pushup(x); pushup(u);
}
void splaynode(int u)
{
while(!isroot(u))
{
int x = spl[u].fa,y = spl[x].fa;
if(!isroot(x)) {x==spl[y].son[1] ^ u==spl[x].son[1] ? rotate(u) : rotate(x);}
rotate(u);
}
pushup(u);
}
void access(int u)
{
for(int i=0; u; i=u,u=spl[u].fa)
{
splaynode(u);
int ls = spl[u].son[0],rs = spl[u].son[1];
spl[u].sz01 += spl[rs].sz11;
spl[u].sz02 += spl[rs].sz11*spl[rs].sz11;
spl[u].ans0 += spl[rs].ans; //not ans0
rs = spl[u].son[1] = i;
spl[u].sz01 -= spl[rs].sz11;
spl[u].sz02 -= spl[rs].sz11*spl[rs].sz11;
spl[u].ans0 -= spl[rs].ans;
pushup(u);
}
}
void link(int u,int v)
{
access(v); splaynode(v);
access(u); splaynode(u); //in order to pushup
spl[u].sz01 += spl[v].sz11;
spl[u].sz02 += spl[v].sz11*spl[v].sz11;
spl[u].ans0 += spl[v].ans;
spl[v].fa = u;
pushup(u);
}
void cut(int u,int v)
{
access(u); splaynode(u);
splaynode(v); //v is in the virtual subtree of u
spl[u].sz01 -= spl[v].sz11;
spl[u].sz02 -= spl[v].sz11*spl[v].sz11;
spl[u].ans0 -= spl[v].ans;
spl[v].fa = 0;
pushup(u);
}
bool isanc(int u,int v) //if u is ancestor of v
{
access(v); splaynode(v);
splaynode(u);
if(!isroot(v)) return true;
return false;
}
void printans(llong x)
{
double ans = (double)x/(double)n/(double)n;
printf("%.12lf\n",ans);
}
int main()
{
scanf("%d",&n);
for(int i=2; i<=n; i++) scanf("%d",&fa[i]);
for(int i=1; i<=n; i++) scanf("%I64d",&a[i]);
for(int i=1; i<=n; i++) spl[i].sz11 = spl[i].sz01 = 1ll,spl[i].ans = spl[i].sum = a[i];
for(int i=2; i<=n; i++)
{
link(fa[i],i);
}
access(1); splaynode(1);
printans(spl[1].ans);
scanf("%d",&q);
for(int i=1; i<=q; i++)
{
char opt[5]; scanf("%s",opt+1);
if(opt[1]=='P')
{
int x,y; scanf("%d%d",&x,&y);
if(isanc(x,y)) {swap(x,y);}
cut(fa[x],x);
fa[x] = y;
link(fa[x],x);
access(1); splaynode(1);
printans(spl[1].ans);
}
else if(opt[1]=='V')
{
int x; llong y; scanf("%d%I64d",&x,&y);
access(x); splaynode(x);
a[x] = y;
pushup(x);
printans(spl[x].ans);
}
}
return 0;
}
Codeforces 482E ELCA (LCT)的更多相关文章
- CF数据结构练习
1. CF 438D The Child and Sequence 大意: n元素序列, m个操作: 1,询问区间和. 2,区间对m取模. 3,单点修改 维护最大值, 取模时暴力对所有>m的数取 ...
- Codeforces 1172E Nauuo and ODT [LCT]
Codeforces ZROI那题是这题删掉修改的弱化版--ZROI还我培训费/px 思路 按照套路,我们考虑每种颜色的贡献,然后发现不包含某种颜色的路径条数更容易数,就是删掉该颜色的点后每个连通块大 ...
- Codeforces 1137F Matches Are Not a Child's Play [LCT]
Codeforces 很好,通过这题对LCT的理解又深了一层. 思路 (有人说这是套路题,然而我没有见过/kk) 首先发现,删点可以从根那里往下删,非常难受,所以把权值最大的点提为根. 然后考虑\(x ...
- Codeforces 1137F - Matches Are Not a Child's Play(LCT)
Codeforces 题面传送门 & 洛谷题面传送门 考虑将一个点 \(x\) 的编号变为当前所有点编号最大值 \(+1\) 会对每个点的删除时间产生怎么样的影响.由于编号最大的点肯定是最后一 ...
- Codeforces 1109F - Sasha and Algorithm of Silence's Sounds(LCT)
Codeforces 题面传送门 & 洛谷题面传送门 讲个笑话,这题是 2020.10.13 dxm 讲题时的一道例题,而我刚好在一年后的今天,也就是 2021.10.13 学 LCT 时做到 ...
- CodeForces gym Nasta Rabbara lct
Nasta Rabbara 题意:简单来说就是, 现在有 n个点, m条边, 每次询问一个区间[ l , r ], 将这个区间的所有边都连上, 如果现在的图中有奇数环, 就输出 “Impossibl ...
- Codeforces Round #539 (Div. 1) 1109F. Sasha and Algorithm of Silence's Sounds LCT+线段树 (two pointers)
题解请看 Felix-Lee的CSDN博客 写的很好,不过最后不用判断最小值是不是1,因为[i,i]只有一个点,一定满足条件,最小值一定是1. CODE 写完就A,刺激. #include <b ...
- 【CF 482E】ELCA
题意 题解 50pts 由于这题 \(2s\),所以可以信仰一波,暴力修改.查询. 暴力修改的复杂度是 \(O(n)\),暴力查询的复杂度是 \(O(n^2)\). 但不难发现可以通过记录子树大小来优 ...
- 【杂题】[CodeForces 1172E] Nauuo and ODT【LCT】【口胡】
Description 给出一棵n个节点的树,每个点有一个1~n的颜色 有m次操作,每次操作修改一个点的颜色 需要在每次操作后回答树上\(n^2\)条路径每条路径经过的颜色种类数和. \(n,m< ...
随机推荐
- python_0基础开始_day12
第十二节 一,生成器 生成器的核心:生成器的本质就是迭代器 迭代器是python自带的 生成器是程序员自己写的一种迭代器 在python中有三种方式来创建生成器: 基于函数编写 推导式方式编写 pyt ...
- python基础数据类型之一
python属于解释型(有良好的平台兼容性,在任何环境中都可以运行,修改代码的时候直接修改就可以,可以快速部署,不用停机维护).动态的(python在编程之前不需要提前设定好各种变量,C语言之类的需要 ...
- 【Java面试题】解释内存中的栈(stack)、堆(heap)和静态存储区的用法
Java面试题:解释内存中的栈(stack).堆(heap)和静态存储区的用法 堆区: 专门用来保存对象的实例(new 创建的对象和数组),实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型 ...
- 深入理解python元类
类也是对象 在理解元类之前,你需要先掌握Python中的类.Python 中的类概念借鉴 Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.当然在 P ...
- 数据绘图工具之Matplotlib
一.安装:绘图和可视化 pip install matplotlib 我们已经下好了anaconda 包含了绘图工具包 直接导入即可 import matplotlib.pyplotlib as pl ...
- cassandra分页
在cassandra的协议中,没有具体规定查询结果的行数限制.但是对于大的数据集,依然有结果分页的必要.过大的结果集会爆掉服务端或者客户端的内存. 传统的分页方法采用了一点trick,采用了token ...
- CentOS6.5增加挂载点容量
一.背景:因为公司虚拟机 (/) 目录容量过小,导致一些任务不能正常执行,需要给虚拟机扩容 二.操作: 初始磁盘情况: 1.使用 df 命令查看磁盘与目录的容量: [root@shaonian ~]# ...
- 两种Tensorflow模型保存的方法
在Tensorflow中,有两种保存模型的方法:一种是Checkpoint,另一种是Protobuf,也就是PB格式: 一. Checkpoint方法: 1.保存时使用方法: tf.train.Sav ...
- Lua语言基本语法~运算符
Lua 变量 变量在使用前,必须在代码中进行声明,即创建该变量. 编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值. Lua 变量有三种类型:全局变量.局部变量.表中的域. ...
- 09Cookie&Session
1.会话技术 1. 会话:一次会话中包含多次请求和响应. 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止2. 功能:在一次会话的范围内的多次请求间,共享数据3. 方式: 1 ...