\(\mathcal{Description}\)

  Link.

  给定一棵含 \(n\) 个点的有根树,点有点权,支持 \(q\) 次操作:

  1. 询问 \(u\) 到根的点权和;
  2. 修改 \(u\) 的父亲,保证得到的图仍是树;
  3. 将 \(u\) 子树内的点权增加一常数。

  \(n\le10^5\),\(q\le3\times10^5\)。

\(\mathcal{Solution}\)

  ETT (Euler Tour Tree),是一种能快速处理子树移动的动态树。本质上,它将树保存作欧拉序,由于子树移动体现在欧拉序上是区间移动,那么就能使用平衡树维护序列。路径查询亦对应了一段区间内仅出现一次的结点贡献和,记每个结点第一次出现为正贡献,后一次出现为负贡献就变成了区间查询,亦能用平衡树维护。本题即 ETT 板题。

  复杂度自然是 \(\mathcal O((n+q)\log n)\) 的。

\(\mathcal{Code}\)

/*~Rainybunny~*/

#include <cstdio>
#include <cassert>
#include <cstdlib> #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i ) typedef long long LL; const int MAXN = 1e5;
int n, ecnt, head[MAXN + 5], w[MAXN + 5], root, st[MAXN + 5], ed[MAXN + 5];
struct Edge { int to, nxt; } graph[MAXN + 5]; inline void link( const int s, const int t ) {
graph[++ecnt] = { t, head[s] }, head[s] = ecnt;
} struct Treap {
static const int MAXND = MAXN << 1;
int node, ch[MAXND + 5][2], fa[MAXND + 5], siz[MAXND + 5], aux[MAXND + 5],
sign[MAXND + 5], ssiz[MAXND + 5];
LL val[MAXND + 5], sum[MAXND + 5], tag[MAXND + 5]; Treap() { srand( 20120712 ); } inline int crtnd( const int v, const int s ) {
int u = ++node;
aux[u] = rand(), siz[u] = 1;
sign[u] = ssiz[u] = s;
val[u] = sum[u] = v * s;
return u;
} inline void pushad( const int u, const LL x ) {
val[u] += sign[u] * x, tag[u] += x, sum[u] += x * ssiz[u];
} inline void pushup( const int u ) {
siz[u] = siz[ch[u][0]] + siz[ch[u][1]] + 1;
ssiz[u] = ssiz[ch[u][0]] + ssiz[ch[u][1]] + sign[u];
sum[u] = sum[ch[u][0]] + sum[ch[u][1]] + val[u];
} inline void pushdn( const int u ) {
if ( tag[u] ) {
if ( ch[u][0] ) pushad( ch[u][0], tag[u] );
if ( ch[u][1] ) pushad( ch[u][1], tag[u] );
tag[u] = 0;
}
} inline int merge( const int u, const int v ) {
if ( !u || !v ) return u | v;
pushdn( u ), pushdn( v );
if ( aux[u] < aux[v] ) {
ch[u][1] = merge( ch[u][1], v );
if ( ch[u][1] ) fa[ch[u][1]] = u;
return pushup( u ), u;
} else {
ch[v][0] = merge( u, ch[v][0] );
if ( ch[v][0] ) fa[ch[v][0]] = v;
return pushup( v ), v;
}
} inline void rsplit( const int u, const int k, int& x, int& y ) {
if ( !u ) return void( x = y = 0 );
fa[u] = 0, pushdn( u );
if ( siz[ch[u][0]] >= k ) {
y = u, rsplit( ch[u][0], k, x, ch[y][0] );
if ( ch[y][0] ) fa[ch[y][0]] = y;
} else {
x = u, rsplit( ch[u][1], k - siz[ch[u][0]] - 1, ch[x][1], y );
if ( ch[x][1] ) fa[ch[x][1]] = x;
}
pushup( u );
} inline int rank( int u ) {
int ret = siz[ch[u][0]] + 1;
while ( fa[u] ) {
if ( ch[fa[u]][1] == u ) ret += siz[ch[fa[u]][0]] + 1;
u = fa[u];
}
return ret;
}
} trp; inline void init( const int u ) {
root = trp.merge( root, st[u] = trp.crtnd( w[u], 1 ) );
for ( int i = head[u]; i; i = graph[i].nxt ) init( graph[i].to );
root = trp.merge( root, ed[u] = trp.crtnd( w[u], -1 ) );
} inline LL query( const int u ) {
int l = trp.rank( st[1] ), r = trp.rank( st[u] ), x, y, z;
assert( l == 1 );
trp.rsplit( root, l - 1, x, y ), trp.rsplit( y, r - l + 1, y, z );
LL ret = trp.sum[y];
root = trp.merge( x, trp.merge( y, z ) );
return ret;
} inline void change( const int u, const int p ) {
int l = trp.rank( st[u] ), r = trp.rank( ed[u] ), tar = trp.rank( st[p] ),
x, y, z, m;
if ( tar < l ) {
trp.rsplit( root, tar, x, y );
trp.rsplit( y, l - tar - 1, y, z );
trp.rsplit( z, r - l + 1, z, m );
} else {
trp.rsplit( root, l - 1, x, y );
trp.rsplit( y, r - l + 1, y, z );
trp.rsplit( z, tar - r, z, m );
}
root = trp.merge( trp.merge( x, z ), trp.merge( y, m ) );
} inline void update( const int u, const int p ) {
int l = trp.rank( st[u] ), r = trp.rank( ed[u] ), x, y, z;
trp.rsplit( root, l - 1, x, y ), trp.rsplit( y, r - l + 1, y, z );
trp.pushad( y, p ), root = trp.merge( x, trp.merge( y, z ) );
} int main() {
scanf( "%d", &n );
for ( int i = 2, t; i <= n; ++i ) scanf( "%d", &t ), link( t, i );
rep ( i, 1, n ) scanf( "%d", &w[i] ); init( 1 ); char op[5]; int q, a, b;
for ( scanf( "%d", &q ); q--; ) {
scanf( "%s %d", op, &a );
if ( op[0] == 'Q' ) printf( "%lld\n", query( a ) );
else if ( op[0] == 'C' ) scanf( "%d", &b ), change( a, b );
else scanf( "%d", &b ), update( a, b );
}
return 0;
}

Solution -「BZOJ #3786」星系探索的更多相关文章

  1. Solution -「BZOJ 3812」主旋律

    \(\mathcal{Description}\)   Link.   给定含 \(n\) 个点 \(m\) 条边的简单有向图 \(G=(V,E)\),求 \(H=(V,E'\subseteq E)\ ...

  2. 【bzoj 3786】星系探索

    Description 物理学家小C的研究正遇到某个瓶颈. 他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球.主星球 ...

  3. Solution -「BZOJ 4316」小C的独立集

    \(\mathcal{Description}\)   Link.   求包含 \(n\) 个结点 \(m\) 条边的仙人掌的最大独立集.   \(n\le5\times10^4\),\(m\le6\ ...

  4. Solution -「BZOJ 3331」压力

    \(\mathcal{Description}\)   Link.   给定一个 \(n\) 个点 \(m\) 条边的连通无向图,并给出 \(q\) 个点对 \((u,v)\),令 \(u\) 到 \ ...

  5. 「BZOJ 4228」Tibbar的后花园

    「BZOJ 4228」Tibbar的后花园 Please contact lydsy2012@163.com! 警告 解题思路 可以证明最终的图中所有点的度数都 \(< 3\) ,且不存在环长是 ...

  6. 「BZOJ 3645」小朋友与二叉树

    「BZOJ 3645」小朋友与二叉树 解题思路 令 \(G(x)\) 为关于可选大小集合的生成函数,即 \[ G(x)=\sum[i\in c ] x^i \] 令 \(F(x)\) 第 \(n\) ...

  7. 「BZOJ 4502」串

    「BZOJ 4502」串 题目描述 兔子们在玩字符串的游戏.首先,它们拿出了一个字符串集合 \(S\),然后它们定义一个字符串为"好"的,当且仅当它可以被分成非空的两段,其中每一段 ...

  8. 「BZOJ 4289」 PA2012 Tax

    「BZOJ 4289」 PA2012 Tax 题目描述 给出一个 \(N\) 个点 \(M\) 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 \(1\) 到点 \( ...

  9. 「BZOJ 2534」 L - gap字符串

    「BZOJ 2534」 L - gap字符串 题目描述 有一种形如 \(uv u\) 形式的字符串,其中 \(u\) 是非空字符串,且 \(v\) 的长度正好为 \(L\), 那么称这个字符串为 \( ...

随机推荐

  1. NIO【同步非阻塞io模型】关于 NIO socket 的详细总结【Java客户端+Java服务端 + 业务层】【可以客户端间发消息】

    1.前言 以前使用 websocket来实现双向通信,如今深入了解了 NIO 同步非阻塞io模型 , 优势是 处理效率很高,吞吐量巨大,能很快处理大文件,不仅可以 做 文件io操作, 还可以做sock ...

  2. SpringBoot学习笔记一之本地环境基础搭建

    原文链接: https://www.toutiao.com/i6802935050196222471/ 工程创建 Maven创建工程 搜索maven找到project的创建 创建simple proj ...

  3. python驱动SAP完成数据导出(一)

    写在前面 我们使用Python驱动SAP时,经常会需要导出一些SAP报表数据至本地Excel文件.这个看似简单的问题背后,其实暗藏玄机,今天小爬就带各位同学好好捋捋. 以事务代码FB03(凭证清单)为 ...

  4. BIO,NIO,AIO 总结

    BIO,NIO,AIO 总结 一.同步阻塞 (BIO) 同步阻塞IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不 做任何事情会造成不必要的线 ...

  5. Go Error 嵌套到底是怎么实现的?

    原文链接: Go Error 嵌套到底是怎么实现的? Go Error 的设计哲学是 「Errors Are Values」. 这句话应该怎么理解呢?翻译起来挺难的.不过从源码的角度来看,好像更容易理 ...

  6. MCU软件最佳实践——使用printf打印数据

    在mcu上开发应用时,使用串口打印调试信息是最常用的调试手段之一.printf是c标准库提供的函数,可以方便输出格式化的信息.但针对不同的mcu芯片,printf函数要能正常工作,需要做一些移植和适配 ...

  7. Android官方文档翻译 十七 4.1Starting an Activity

    Starting an Activity 开启一个Activity This lesson teaches you to 这节课教给你 Understand the Lifecycle Callbac ...

  8. 【reverse】逆向4 初识堆栈

    [reverse]逆向4 初识堆栈 1.问题引入 假设我们需要一块内存,有如下的要求 主要用于临时存储一些数据(如果数据很少可以放入寄存器中) 能够记录存了多少数据 能够非常快速的找到某个数据 2.模 ...

  9. 华为matebook x pro蓝屏和拆机更换固态硬盘

    华为老版本的笔记本电脑现在总是蓝屏. 情况 原因 我个人认为是建兴的固态硬盘的缘故. 我的笔记本几乎没用过,因为考研.如果玩游戏使用的老ThinkPad S5.matebook我这个丐版因为没有独立显 ...

  10. 使用Hot Chocolate和.NET 6构建GraphQL应用(1)——GraphQL及示例项目介绍

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 前言 这篇文章是这个系列的第一篇,我们会简单地讨论一下GraphQL,然后介绍一下这个系列将会使用的示例项目. 关 ...