@description@

九条可怜是一个热爱阅读的女孩子。

这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣。

这个世界有 n 个城市,这 n 个城市被恰好 n-1 条双向道路联通,即任意两个城市都可以互相到达。同时城市 1 坐落在世界的中心,占领了这个城市就称霸了这个世界。

在最开始,这 n 个城市都不在任何国家的控制之下,但是随着社会的发展,一些城市会崛起形成国家并夺取世界的霸权。为了方便,我们标记第 i 个城市崛起产生的国家为第 i 个国家。 在第 i 个城市崛起的过程中,第 i 个国家会取得城市 i 到城市 1 路径上所有城市的控制权。

新的城市的崛起往往意味着战争与死亡,若第 i 个国家在崛起中,需要取得一个原本被国家 j(j ≠ i) 控制的城市的控制权,那么国家 i 就必须向国家 j 宣战并进行战争。

现在,可怜知道了,在历史上,第 i 个城市一共崛起了 ai 次。但是这些事件发生的相对顺序已经无从考究了,唯一的信息是,在一个城市崛起称霸世界之前,新的城市是不会崛起的。

战争对人民来说是灾难性的。可怜定义一次崛起的灾难度为崛起的过程中会和多少不同的国家进行战争(和同一个国家进行多次战争只会被计入一次)。可怜想要知道,在所有可能的崛起顺序中,灾难度之和最大是多少。

同时,在考古学家的努力下,越来越多的历史资料被发掘了出来,根据这些新的资料,可怜会对 ai 进行一些修正。具体来说,可怜会对 ai 进行一些操作,每次会将 ax 加上 w。她希望在每次修改之后,都能计算得到最大的灾难度。

然而可怜对复杂的计算并不感兴趣,因此她想让你来帮她计算一下这些数值。

对题面的一些补充:

同一个城市多次崛起形成的国家是同一个国家,这意味着同一个城市连续崛起两次是不会和任何国家开战的:因为这些城市原来就在它的控制之下。

在历史的演变过程中,第 i 个国家可能会有一段时间没有任何城市的控制权。但是这并不意味着第 i 个国家灭亡了,在城市 i 崛起的时候,第 i 个国家仍然会取得 1 到 i 路径上的城市的控制权。

输入格式

第一行输入两个整数 n,m 表示城市个数和操作个数。

第二行输入 n 个整数表示 ai 的初始值。 接下来 n − 1 行,每行输入两个整数 ui, vi(1≤ui,vi≤n) 描述了一条道路。

接下来 m 行每行输入两个整数 xi, wi 表示将 a[xi] 加上 wi。

输出格式

输出共 m+1 行,第一行表示初始的 ai 的答案,接下来 m 行每行表示这次修正后的答案。

样例输入

5 3

1 1 1 1 1

1 2

1 3

2 4

2 5

2 1

3 1

4 1

样例输出

6

7

9

10

样例解释

在修正开始之前,如果按照所在城市 4, 1, 5, 3, 2 的顺序崛起,那么依次会和 0, 1, 2, 1, 2 个 国家进行战争。

这时一共会产生 6 对敌对关系。可以证明这是所有崛起顺序中的最大值。

数据范围与提示

n, m <= 4*10^5。

@solution@

就是一个 link-cut-tree 中 access 过程经过的颜色段的种类数。等价地,我们统计往上爬的过程颜色第一次的出现位置个数。

怎么让它最大呢?我们不妨考虑每一个点 x 的贡献。如果相邻操作的两个点来自 x 的相同子树,则点 x 不会产生贡献。

也就是说,x 是否产生贡献只跟操作的点 p 在 x 的哪个子树有关(也可以是 x 本身),与 p 本身是什么无关。

记 sum[x] 为以 x 为根的子树内 ai 之和,再记 mxs[x] = max{max{sum[u]}, a[x]}。

当 2*mxs[x] <= sum[x] 时,可以安排 sum[x] 这些操作相邻的两两不来自于同一子树,此时最大贡献为 sum[x] - 1;否则,此时最大贡献为 2*(sum[x] - mxs[x])。

注意第一次不算,因为第一次的时候 x 还没有颜色。

这样子,我们就可以算出没有修改的贡献。

考虑怎么在修改中维护这个值,因为它本身就是一个 access 操作,所以不难想到使用 lct。

当 2*mxs[x] > sum[x] 时,如果同时让 sum[x] 与 mxs[x] 加 w,则不等式依然不变,且贡献依然不变。

于是:我们令 mxs[x] 对应的儿子(注意 mxs[x] 还可能对应 x 自己,这时就不考虑)为 x 的重儿子,然后 link-cut-tree 一波。

此时轻边要考虑轻重边的变化是否满足要求,不能像平时的 lct 一样直接就重边接上去。重链用 tag 维护一下即可。

复杂度?每次跳轻边,sum 至少翻倍,所以最多跳 O(log) 次轻边。

其实说是 link-cut-tree,不如说是树链剖分的动态维护。。。

@accepted code@

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 400000;
ll ans;
struct link_cut_tree{
struct node{
ll sum, mxs, tg, key;
node *ch[2], *fa;
int dir() {return fa->ch[1] == this;}
}pl[MAXN + 5], *ncnt, *NIL;
void maintain(node *x, ll k) {
if( x == NIL ) return ;
x->tg += k, x->sum += k, x->mxs += k;
}
void pushdown(node *x) {
if( x->tg ) {
maintain(x->ch[0], x->tg);
maintain(x->ch[1], x->tg);
x->tg = 0;
}
}
link_cut_tree() {
ncnt = NIL = &pl[0];
NIL->ch[0] = NIL->ch[1] = NIL->fa = NIL;
NIL->sum = NIL->mxs = NIL->tg = 0;
}
node *newnode() {
node *p = (++ncnt);
p->ch[0] = p->ch[1] = p->fa = NIL;
p->sum = p->mxs = p->tg = 0;
return p;
}
void set_child(node *x, node *y, int d) {
if( x != NIL ) x->ch[d] = y;
if( y != NIL ) y->fa = x;
}
bool is_root(node *x) {
return x->fa->ch[0] != x && x->fa->ch[1] != x;
}
void rotate(node *x) {
node *y = x->fa; int d = x->dir();
pushdown(y), pushdown(x);
if( is_root(y) ) x->fa = y->fa;
else set_child(y->fa, x, y->dir());
set_child(y, x->ch[!d], d);
set_child(x, y, !d);
}
void splay(node *x) {
pushdown(x);
while( !is_root(x) ) {
node *y = x->fa;
if( is_root(y) ) rotate(x);
else {
if( y->dir() == x->dir() )
rotate(y);
else rotate(x);
rotate(x);
}
}
}
node *find(node *x) {
if( x == NIL ) return NIL;
node *y = x;
while( y->ch[0] != NIL ) pushdown(y), y = y->ch[0];
splay(y);
return y;
}
void update(node *x, int k) {
node *y = NIL; x->key += k;
// printf("! %d\n", k);
while( x != NIL ) {
splay(x);
if( x->sum ) ans -= min(x->sum - 1, 2*(x->sum - x->mxs));
x->sum += k;
if( 2*x->mxs <= x->sum )
x->ch[1] = NIL;
y = find(y);
if( x->mxs < y->sum ) {
x->mxs = y->sum;
if( 2*x->mxs > x->sum )
x->ch[1] = y;
}
if( x->mxs < x->key ) {
x->ch[1] = NIL;
x->mxs = x->key;
}
maintain(x->ch[0], k);
// printf("%d : %lld %lld \t | \t %d : %lld %lld\n", x - pl, x->mxs, x->sum, y - pl, y->mxs, y->sum);
ans += min(x->sum - 1, 2*(x->sum - x->mxs));
y = x, x = x->fa;
}
// printf("%lld\n", ans);
}
}T;
vector<int>G[MAXN + 5];
void addedge(int u, int v) {
G[u].push_back(v), G[v].push_back(u);
}
int a[MAXN + 5];
link_cut_tree::node *nd[MAXN + 5];
void dfs(int x, int f) {
nd[x] = T.newnode();
if( x != 1 ) nd[x]->fa = nd[f];
T.update(nd[x], a[x]);
for(int i=0;i<G[x].size();i++) {
int p = G[x][i];
if( p != f ) dfs(p, x);
}
}
int main() {
int n, m; scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++)
scanf("%d", &a[i]);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
dfs(1, 0);
printf("%lld\n", ans);
for(int i=1;i<=m;i++) {
int x, w; scanf("%d%d", &x, &w);
T.update(nd[x], w);
printf("%lld\n", ans);
}
}

@details@

splay 的父子关系并不对应原树中的父子关系!!!

函数参数列表中不要把 long long 写成 int!!!!

@loj - 2434@ 「ZJOI2018」历史的更多相关文章

  1. LOJ #2434. 「ZJOI2018」历史(LCT)

    题意 click here 题解 我们首先考虑答案是个什么样的东西, 不难 发现每个点可以单独计算它的贡献. 令每个点 \(i\) 崛起次数为 \(a_i\) . 假设一个点子树的 \(\sum a_ ...

  2. 「ZJOI2018」历史(LCT)

    「ZJOI2018」历史(LCT) \(ZJOI\) 也就数据结构可做了-- 题意:给定每个点 \(access\) 次数,使轻重链切换次数最大,带修改. \(30pts:\) 挺好想的.发现切换次数 ...

  3. 「ZJOI2018」历史

    「ZJOI2018」历史 前置知识 \(\text{LCT}\) 维护子树信息,考虑辅助树上一个节点的子树信息只是其代表的这一段链的信息,设 \(S(u)\) 为节点 \(u\) 的子树信息,那么在辅 ...

  4. Loj #2529. 「ZJOI2018」胖

    Loj #2529. 「ZJOI2018」胖 题目描述 Cedyks 是九条可怜的好朋友(可能这场比赛公开以后就不是了),也是这题的主人公. Cedyks 是一个富有的男孩子.他住在著名的 The P ...

  5. LOJ2434. 「ZJOI2018」历史 [LCT]

    LOJ 思路 第一眼看似乎没有什么思路,试着套个DP上去:设\(dp_x\)表示只考虑\(x\)子树,能得到的最大答案. 合并的时候发现只有\(x\)这个点有可能做出新的贡献,而做出新贡献的时候必然是 ...

  6. 题解 「ZJOI2018」历史

    题目传送门 Description 九条可怜是一个热爱阅读的女孩子. 这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣. 这个世界有 \(n\) 个城市,这 \(n\) 个城市被恰 ...

  7. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  8. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  9. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

随机推荐

  1. JAVA--养成好的编程习惯

    原文地址:http://user.qzone.qq.com/1159340782/2 略微牺牲一些性能,换来更好的可读性和可维护性是好的选择.以下不提倡和提倡的写法仅供参考. 代码中不提倡的写法 序号 ...

  2. LINNX查看当前登录的用户

    W w命令主要是查看当前登录的用户,这个命令相对来说比较简单.我们来看一下截图. 在上面这个截图里面呢,第一列user,代表登录的用户,第二列,tty代表用户登录的终端号,因为在linux中并不是只有 ...

  3. 洛谷P1970 [NOIP2013提高组Day2T2] 花匠

    P1970 花匠 题目描述 花匠栋栋种了一排花,每株花都有自己的高度.花儿越长越大,也越来越挤.栋栋决定 把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希 望剩下的花排 ...

  4. golang在import自己的包报错问题

    原因:使用git clone项目后,项目根路径是小写英文名称,比如cmdbapi,但是项目里面的import导入自己的相关包时,红色报错 解决:把项目名称改写成import导入包的名称,即cmdbAp ...

  5. js 正则去除html代码

    function delHtmlTag(str){ return str.replace(/<[^>]+>/g,"");//去掉所有的html标记 }

  6. jquery 调用asp.net后台代码

    1.需要引用对应的命名空间 System.Web.Services 2.后台方法: 必须是static 约束   必须添加[WebMethod()] 属性 示例: <script type=&q ...

  7. ML面试1000题系列(51-60)

    本文总结ML面试常见的问题集 转载来源:https://blog.csdn.net/v_july_v/article/details/78121924 51.简单说下sigmoid激活函数 常用的非线 ...

  8. Ajax--art-template + 调用天气接口

    一.实现原理: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  9. TSP

    #include <iostream> #include <limits.h> #include <string.h>; using namespace std; ...

  10. JS---案例:旋转木马

    案例:旋转木马 页面加载时候出现的效果,script标签写在head里面,body上面 显示一个图片散开的动画,遍历之后,把每个图片用封装的动画函数移动到指定目标(同时改变多属性:宽,透明度,层级,t ...