@loj - 2434@ 「ZJOI2018」历史
@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」历史的更多相关文章
- LOJ #2434. 「ZJOI2018」历史(LCT)
题意 click here 题解 我们首先考虑答案是个什么样的东西, 不难 发现每个点可以单独计算它的贡献. 令每个点 \(i\) 崛起次数为 \(a_i\) . 假设一个点子树的 \(\sum a_ ...
- 「ZJOI2018」历史(LCT)
「ZJOI2018」历史(LCT) \(ZJOI\) 也就数据结构可做了-- 题意:给定每个点 \(access\) 次数,使轻重链切换次数最大,带修改. \(30pts:\) 挺好想的.发现切换次数 ...
- 「ZJOI2018」历史
「ZJOI2018」历史 前置知识 \(\text{LCT}\) 维护子树信息,考虑辅助树上一个节点的子树信息只是其代表的这一段链的信息,设 \(S(u)\) 为节点 \(u\) 的子树信息,那么在辅 ...
- Loj #2529. 「ZJOI2018」胖
Loj #2529. 「ZJOI2018」胖 题目描述 Cedyks 是九条可怜的好朋友(可能这场比赛公开以后就不是了),也是这题的主人公. Cedyks 是一个富有的男孩子.他住在著名的 The P ...
- LOJ2434. 「ZJOI2018」历史 [LCT]
LOJ 思路 第一眼看似乎没有什么思路,试着套个DP上去:设\(dp_x\)表示只考虑\(x\)子树,能得到的最大答案. 合并的时候发现只有\(x\)这个点有可能做出新的贡献,而做出新贡献的时候必然是 ...
- 题解 「ZJOI2018」历史
题目传送门 Description 九条可怜是一个热爱阅读的女孩子. 这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣. 这个世界有 \(n\) 个城市,这 \(n\) 个城市被恰 ...
- Loj #2192. 「SHOI2014」概率充电器
Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...
- Loj #3096. 「SNOI2019」数论
Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...
- Loj #3093. 「BJOI2019」光线
Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...
随机推荐
- Centos Apache 多站点配置
首先明白APACHE配置文件位置 /etc/httpd/ 系统会自动加载 "/etc/httpd/conf.d" 目录下面的 "*.conf"文件 创建多个 & ...
- Hackerrank--String Function Calculation(后缀数组)
题目链接 Jane loves string more than anything. She made a function related to the string some days ago a ...
- Hackerrank--Emma and sum of products (FFT)
题目链接 Emma is really fond of integers and loves playing with them. Her friends were jealous, and to t ...
- JPinyin繁体相互转换
// 用正则表达式"[\u4e00-\u9fa5]"匹配 字符串 Scanner sc =new Scanner(System.in);System.out.println(&qu ...
- spring cloud深入学习(一)-----什么是微服务?什么是rpc?spring cloud简介
近年来,微服务非常的流行,那么为什么是它?简单介绍一下. 为什么是微服务? 微服务架构是一种将单应用程序作为一套小型服务开发的方法,每种应用程序都在其自己的进程中运行,并与轻量级机制(通常是HTTP资 ...
- Java的split()方法
在java中,split方法很常用,但是有些时候我们发现并不管用,不管用的情况:split("."),split("^"), 管用的情况:split(" ...
- ubuntu 12.4,搞定apt源
http://wiki.ubuntu.org.cn/Template:12.04source deb http://cn.archive.ubuntu.com/ubuntu/ precise main ...
- 在Spring应用中创建全局获取ApplicationContext对象
在Spring应用中创建全局获取ApplicationContext对象 1.需要创建一个类,实现接口ApplicationContextAware的setApplicationContext方法. ...
- MySQL--python关联MySQL、练习题
1.python关联MySQL pymysql: 安装:pip3 install pymysql 1.0:连接到数据库中 import pymysql conn = pymysql.connect( ...
- web前端学习(三)css学习笔记部分(2)-- css定位+盒子操作
3.CSS定位 3.1定位 1.CSS定位: 改变元素在页面上的位置 2.CSS定位机制 普通流:元素按照其在HTML中的位置顺序决定排布的过程 浮动 绝对布局 属性 描述 position 把元素放 ...