@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 ...
随机推荐
- 【noip】跟着洛谷刷noip题2
noip好难呀. 上一个感觉有点长了,重开一个. 36.Vigenère 密码 粘个Openjudge上的代码 #include<cstdio> #include<iostream& ...
- 移动端页面输入法挡住input输入框的解决方法
1,宽高用了百分比或者vw/vh布局的,input输入框的最外层父容器的可用JS动态设置为当前窗口的宽高(防止输入法的弹出令页面变形) 2,最外层父容器用了fixed定位的,不要用top:0;要用bo ...
- Dockerfile 编写
转: https://blog.fundebug.com/2017/05/15/write-excellent-dockerfile/如何编写最佳的Dockerfile 译者按: Dockerfile ...
- WPF内嵌CEF控件,与JS交互
1)安装cefsharp.winform包 打开VS2017,打开nuget,找到cefsharp.winform,安装 问:为什么wpf程序不使用cefsharp.wpf? 答:因为cefwpf 4 ...
- 各NoSQL数据库管理系统与模型比较
提供:ZStack云计算 内容简介 NoSQL数据库的存在意义在于提供传统关系数据库管理系统所不具备的特定功能.无论是负责承载简单的键-值对存储以实现短期缓存,抑或是处理传统数据库及结构化查询语言(简 ...
- Python学习(二) 基础语法之初看python
Python 标识符 略 Python保留字符 一大堆,说了未必记得住,编码过程中慢慢去记住. 行和缩进 这个要说一下,学习Python与其他语言最大的区别就是,Python的代码块不使用大括号({} ...
- Vue. 之 Element table 单元格内容隐藏
Vue. 之 Element table 单元格内容隐藏 在table显示数据时,若某个单元格的内容过多,需要进行隐层,在这一列的单元格属性添加::show-overflow-tooltip=&quo ...
- day36 04-Hibernate检索方式:多表连接查询
返回的是一个List集合,这个List集合的泛型是一个Object数组.最后会拿到一个里面放Object数组的List集合. HQL内连接查询,发出SQL语句查询出来的结果集被Hibernate封装成 ...
- golang之结构体
Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. Go语言通过用自定义的方式形成新的类型,结构体是类型中带有成员的复合类型. Go 语言中的类型可以被实例化,使用new或&a ...
- spring源码学习之默认标签的解析(二)
这个是接着上一篇来写,主要是这章内容比较多,还是分开来写吧! 一.AbstractBeanDefinition属性介绍 XML中的所有的属性都可以在GenericBeanDefinition中找到对应 ...