「ZJOI2018」历史

「ZJOI2018」历史

前置知识

\(\text{LCT}\) 维护子树信息,考虑辅助树上一个节点的子树信息只是其代表的这一段链的信息,设 \(S(u)\) 为节点 \(u\) 的子树信息,那么在辅助树上我们维护的是:
\[
S(u)=S(lson)+S(rson)+val(u)
\]
考虑它们的实际意义 \(lson\) 是 \(u\) 的父亲,\(rson\) 是 \(u\) 的重儿子,显然 \(S(lson)\) 是我们不需要的,而真正的辅助信息只算了节点本身和重儿子。

考虑按照这样算的话, \(u\) 的每一个轻儿子代表的子树信息都是合法的,那么可以再开一个 \(vs(u)\) 把所有轻儿子的 \(S\) 加上去。
\[
S(u)=S(lson)+S(rson)+val(u)+vs(u)
\]
轻重链切换的时候暴力更新一下 \(vs(u)\) 即可,动态加边的话因为要影响虚边,要把两个点都 \(\text{makeroot}\) 来保证更新的 \(vs(u)\) 合法,删边的话因为保证了关系是重儿子,所以直接做就好了,\(access\) 切换轻重边的时候需要重新维护一下。

但这种做法适用范围不大,信息不可减就做不了,只能单点不断条重链修改,然而对这道题来说已经足够了。

解题思路

观察发现每个节点的贡献是独立的,同一个儿子内的连续多次崛起不会贡献新的代价。显然要最大化每个节点的贡献需要尽可能两次崛起位于不同的儿子的子树。

设节点 \(u\) 的子树 \(a_i\) 和为 \(sz(u)\) ,\(mson=\max(\max_{u\rightarrow v}sz(v), a(u))\) 。

如果 \(2mson\leq sz(u)\) ,那么显然可以构造出操作序列满足不会有连续相同,贡献就是 \(sz(u)-1\)。

否则可以贪心的考虑,尽可能多用 \(mson\) ,节点 \(u\) 的贡献就是 \(2(sz(u)-mson)\)。

如果 \(u\) 的一个儿子 \(v\) 满足 \(2sz(v)>sz(u)\) ,那么我们记 \(u\rightarrow v\) 这条边为重边,其余边为轻边,类似于树链剖分和 \(\text{LCT}\) 的结构。

然后我们可以得到一些基本推论。

  1. 一个节点向下只会至多有一条重边。
  2. 一个节点到根的路径上只会至多有 \(log_{na_i}\) 条轻边,因为每有一条轻边 \(sz(u)\) 至少减少二分之一。

观察发现每一次修改操作,路径上的重边仍旧是重边,所以贡献不变,路径上的轻边可能会发生改变,只需要找到这些轻边暴力改就好了。

前面说了这个结构和 \(\text{LCT}\) 很类似,只需要每条重链用辅助树维护,支持单点加求一个点在根为 \(1\) 时的子树和,以及找一个点到一条路径上的轻边。

前面两个要求 \(\text{LCT}\) 维护子树信息可以直接做,查询只需要类似 \(access\) 走一遍,但是不要强制断开原有的重边,而是对于每条轻边按照前置知识的方法更新子树和再判断,细节有点多。

复杂度的话,单次修改 \(\text{splay}\) 重链条数次,轻边条数是 \(O(log_{na_i})\) ,和正常的 \(\text{LCT}\) 没啥区别,套用那个的均摊分析就是 \(\mathcal O(nlogn)\) 。

code

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 800005;
vector<int> g[N];
int ch[N][2], fa[N], n, m;
ll vs[N], a[N], s[N], ans;
#define son(x) (ch[fa[x]][1] == x)
#define isr(x) (ch[fa[x]][0] != x && ch[fa[x]][1] != x)
inline void update(int u){ s[u] = s[ch[u][0]] + s[ch[u][1]] + a[u] + vs[u]; }
inline void rotate(int x){
    int F = fa[x], G = fa[F], w = son(x);
    if(!isr(F)) ch[G][son(F)] = x; fa[x] = G;
    ch[F][w] = ch[x][w^1], fa[ch[x][w^1]] = F;
    ch[x][w^1] = F, fa[F] = x, update(F);
}
inline void splay(int x){
    for(int F = fa[x]; !isr(x); rotate(x), F = fa[x])
        if(!isr(F)) rotate(son(F) == son(x) ? F : x);
    update(x);
}
inline ll calc(int x, ll t, ll h){
    return ch[x][1] ? ((t - h) << 1) : ((a[x] << 1) > t ? ((t - a[x]) << 1) : t - 1);
}
inline void access(int x, int w){
    splay(x);
    ll now = s[x] - s[ch[x][0]], mson = s[ch[x][1]];
    ans -= calc(x, now, mson), s[x] += w, a[x] += w, now += w;
    if((mson << 1) <= now) vs[x] += mson, ch[x][1] = mson = 0;
    ans += calc(x, now, mson), update(x);
    int c = x; x = fa[x];
    for(; x; c = x, x = fa[x]){
        splay(x);
        assert(isr(x));
        ll now = s[x] - s[ch[x][0]], mson = s[ch[x][1]];
        ans -= calc(x, now, mson), s[x] += w, vs[x] += w, now += w;
        if((mson << 1) <= now) vs[x] += mson, ch[x][1] = mson = 0;
        if((s[c] << 1) > now) vs[x] -= (mson = s[c]), ch[x][1] = c;
        ans += calc(x, now, mson), update(x);
    }
}
inline void dfs(int u){
    s[u] = a[u];
    for(int i = 0; i < (int) g[u].size(); i++)
        if(g[u][i] != fa[u]) fa[g[u][i]] = u, dfs(g[u][i]), s[u] += s[g[u][i]];
    for(int i = 0; i < (int) g[u].size(); i++)
        if(g[u][i] != fa[u] && s[g[u][i]] * 2 > s[u]) ch[u][1] = g[u][i];
    vs[u] = s[u] - a[u] - s[ch[u][1]];
    ans += calc(u, s[u], s[ch[u][1]]);
}
int main(){
    read(n), read(m);
    for(int i = 1; i <= n; i++) read(a[i]);
    for(int i = 1, x, y; i < n; i++){
        read(x), read(y);
        g[x].push_back(y), g[y].push_back(x);
    }
    dfs(1);
    printf("%lld\n", ans);
    for(int i = 1, x, y; i <= m; i++){
        read(x), read(y), access(x, y);
        printf("%lld\n", ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/mangoyang/p/10472903.html

时间: 2024-08-02 08:07:06

「ZJOI2018」历史的相关文章

从「集装箱」思考Docker风潮

从「集装箱」思考Docker风潮 -- Docker潮流下的赢家策略 By 高焕堂 (台灣Docker聯盟 主席) 2015/02/20 前言 在许多革命性转折里,经常出现集装箱的身影:它就像幸运草一般,总是带来许多幸福和财运.现在Docker风起云涌,再现集装箱身影,如果开放视野.大力支持它,持续发挥它的潜能和力量,则幸运草就会出现在我们身旁了. 由于Docker集装箱带来的商机,其最直接的受益者是软件管理者(或称维运者),例如软件测试工具业者.测试人员等.因此在今天,不论您是开发者或是维运者

分布式系统「伸缩性」大招之——「水平&amp;垂直切分」详解

如果第二次看到我的文章,欢迎右侧扫码订阅我哟~  ?? 本文长度为5389字,建议阅读14分钟. 坚持原创,每一篇都是用心之作- 没想到这篇文章写了这么长,一时半会没消化完的话,可以收藏一下先. 这是「伸缩性」章节的第四篇,先给新来的小伙伴们简单回顾下前三篇的内容. 做「伸缩性」最重要的就是先做好「无状态」,如此才可以随心所欲的进行横向“扩展”,而不用担心在多个副本之间切换会产生错乱.<分布式系统关注点——「无状态」详解>聊的就是这个. 不过,就算做好了横向扩展,本质上还是一个“大程序”,只是

「mac」释放 macOS 菜单栏潜能的软件们(十四款) 19.3.13 删除一款

转至:持续文章更新列表,建议收藏 一款好的软件不但可以节约时间,更能让你体验系统的魅力. 想知道我的 Mac 菜单栏都有什么嘛?这是一篇简单的介绍 Mac 菜单栏工具的文章,共计 15 款,每一款点击都可以直达官网. 大多数软件都提供适用版本,建议先行试用在决定是否购买,如没有试用版可以回复你的问题询问详情! 只有最适合你的应用,才称得上是效率应用. 尝新者:尝试一切新鲜的事物 题图就是我的菜单栏啦! 要知道菜单栏堆积,的确就如同 iOS 上软件从不排序一样令人不快,无法快速找到想要的对应应用,

JavaScript 引擎「V8」发布 8.0 版本,内存占用量大幅下降

上周,JavaScript 引擎「V8」的开发团队在该项目官方网站上正式宣布推出最新的 8.0 版本.这次更新的重点主要集中在错误修复及性能改善上,正式的版本将在数周后随着谷歌 Chrome 80 稳定版一起发布. V8 是谷歌公司推出的开源高性能 JavaScript 引擎,主要用于提升 Web 浏览器内部 JavaScript 脚本执行的性能.V8 通过 C++ 语言编写,主要用在 Chrome 浏览器以及 Node.js 上,实现了对 ECMAScript 与 WebAssembly 的支

「MoreThanJava」计算机发展史—从织布机到IBM

「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 不论新老朋友 我相信您都可以 从中获益.如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! 一.织布机 | 一切的开端 如今代表智能现代的计算机与老式织布机的血缘关系超乎你的想象.无论是摆在写字台上的 台式机.塞在口袋里的 掌上电脑.

AC日记——「HNOI2017」单旋 LiBreOJ 2018

#2018. 「HNOI2017」单旋 思路: set+线段树: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxtree maxn<<2 int val[maxtree],tag[maxtree],L[maxtree],R[maxtree],mid[maxtree]; int op[maxn],ki[maxn],bi[maxn],cnt,size,n,ch[maxn]

「随笔」基于当下的思考

马德,说好的技术blog,变成日记本了... 下午的时候莫名其妙的感到很颓废,因为自己的不够强大感到忧虑和危机感十足.现在每每行走在技术的道路上,常觉得如履薄冰,如芒在背. 上大学之前和现在的心态其实差别挺大的,视野的开阔远远不止局限于自己的脚下.不过,这里的「上大学之前」只是一个时间描述词,并不觉得大学是最适合学习的地方,我很失望. 世界上的人无论性别,区域,宗教,兴趣爱好,总可以在互联网上找到志趣相同的人,总是可以不断打破自己的常识与惯性思维.总是有在相同领域比自己更强的人,挺好的. 关于知

「Unity」与iOS、Android平台的整合:3、导出的Android-Studio工程

本文属于「Unity与iOS.Android平台的整合」系列文章之一,转载请注明出处. Unity默认导出的是Android-Eclipse工程,毕竟Eclipse for Android开发在近一两年才开始没落,用户量还是非常巨大的. 个人认为AndroidStudio非常好用,能轻易解决很多Eclipse解决不了或者很难解决的问题. 所以我将Unity导出的Andoid工程分为Eclipse和AndroidStudio两部分. 不过我之后的相关内容都会使用AndroidStudio,希望依然

大数据和「数据挖掘」是何关系?---来自知乎

知乎用户,互联网 244 人赞同 在我读数据挖掘方向研究生的时候:如果要描述数据量非常大,我们用Massive Data(海量数据)如果要描述数据非常多样,我们用Heterogeneous Data(异构数据)如果要描述数据既多样,又量大,我们用Massive Heterogeneous Data(海量异构数据)--如果要申请基金忽悠一笔钱,我们用Big Data(大数据) 编辑于 2014-02-2817 条评论感谢 收藏没有帮助举报作者保留权利 刘知远,NLPer 4 人赞同 我觉得 大数据