@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 ...
随机推荐
- Sublime setting 个性修改
{ // 编辑器字体大小 "font_size": 10, // 设置行间距 "line_padding_bottom":5, "line_paddi ...
- 从零开始Android逆向教程(二)——什么是Xposed
前言在阅读本文之前,假设你的手机已经root,并且已经成功安装好了 XposedInstaller. Xposed是什么? Xposed 是一个 Android 平台上的动态劫持框架,通过 ...
- SPSS正交设计的操作
SPSS正交设计的操作 设要做二因素的正交设计,A因素有三个水平,B因素有两个水平.则选择Data-->Orthogonal Design-->generate,弹出的就是正交设计窗口: ...
- Vue.之.创建项目
Vue.之.创建项目 第一次使用vue的时候,在已完成node的情况下,还需要在进行安装vue. 指令:cnpm install vue-cli -g //全局安装 vue-cli 检查vu ...
- day38 09-Spring类的完整生命周期及后处理Bean
可以配置Bean的这个类的初始化和销毁的方法. 如何销毁这个bean?销毁必须得手动地关闭掉容器才行.而且销毁必须是在scope="singleton"下才有效.因为如果你scop ...
- Redhat/Fedora 网络接口的配置文件和网络接口专用配置工具
在Redhat/Fedora 中,与乙太网卡相关的配置文件位于 /etc/sysconfig/network-scripts目录中,比如 ifcfg-eth0.ifcfg-eth1 .... .... ...
- 【洛谷】P1427 小鱼的猜数游戏
P1427 小鱼的数字游戏 题目描述 小鱼最近被要求参加一个数字游戏,要求它把看到的一串数字(长度不一定,以0结束,最多不超过100个,数字不超过2^32-1),记住了然后反着念出来(表示结束的数字0 ...
- 阿里云DMS发布数据库网关服务: 打通网络限制 开启数据库统一管理的万能钥匙
概述 阿里云数据管理DMS在云端可提供专业的数据库服务,除对标本地数据库软件的基础功能外,还包含性能诊断.数据追踪.跨实例SQL查询(含异构数据库类型之间)等专业性功能,同时提供审计安全和企业级数据库 ...
- 学习JDK1.8集合源码之--HashSet
1. HashSet简介 HashSet是一个不可重复的无序集合,底层由HashMap实现存储,故HashSet是非线程安全的,由于HashSet使用HashMap的Key来存储元素,而HashMap ...
- 洛谷P1541 乌龟棋 [2010NOIP提高组]
P1541 乌龟棋 题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家 ...