\(\color{#0066ff}{ 题目描述 }\)

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

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

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

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

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

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

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

  • 同一个城市多次崛起形成的国家是同一个国家,这意味着同一个城市连续崛起两次是不会 和任何国家开战的:因为这些城市原来就在它的控制之下。
  • 在历史的演变过程中,第 i 个国家可能会有一段时间没有任何城市的控制权。但是这并不 意味着第 i 个国家灭亡了,在城市 i 崛起的时候,第 i 个国家仍然会取得 1 到 i 路径上的城市的控制权。

\(\color{#0066ff}{输入格式}\)

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

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

接下来 m 行每行输入两个整数 \(x_i\), \(w_i\) 表示将 \(a_{x_i}\) 加上 \(w_i\)。

\(\color{#0066ff}{输出格式}\)

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

\(\color{#0066ff}{输入样例}\)

5 3
1 1 1 1 1
1 2
1 3
2 4
2 5
2 1
3 1
4 1

\(\color{#0066ff}{输出样例}\)

6
7
9
10

\(\color{#0066ff}{数据范围与提示}\)

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

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

\(\color{#0066ff}{ 题解 }\)

简单来说,题意就是给你每个点可以access的次数,还有修改,让你找到最优的access的顺序,使得虚实变换的次数最多

考虑每个点的子树。假设已经对所有子树中的点构造出了一个最优顺序(一个序列),那么一定不会和它的所有祖先的子树中的最优序列产生冲突。

所以我们考虑一个点x,能对x的实子边产生影响的,是x的所有子树和x本身(access(x)后x无实子边)

每次切换会对ans有1的贡献,显然同一子树的影响是相同的

因此我们让不同的子树或x交替进行access,这样产生的贡献才最多

当没有某棵子树的a之和或者x的a过大时,可以构造出(除了第一次)每个access都用贡献的方案

反之,当总和大于所有子树总和的一半时,就不行了,a之和特别大的那个子树(或x)一定有几次access是没有贡献的,设S为子树a之和,c为x的每棵子树,则贡献为

\[\min\{S_x-1,2*(S_x-\max\{a_x,\forall S_c\})\}
\]

这样DP就能把不带修改的分拿到了

现在考虑修改

首先,对于x的a加上w,只会对x到根的路径上的节点产生影响

因为我们只是加上一个值,所以如果某些子树的a之和大于一半,把a带进去发现贡献是不变的

某个子树的a之和大于总和一半???这是树剖的轻重边划分啊

所以如果某个子树的a之和大于总和一半,就连重边,否则就是轻边

于是我们在全局保存一个ans,access的时候找到虚边,直接减去原来的贡献,然后用w进行修改, 判断是否要切换虚实边,并让ans加上新的贡献即可

可以设一个type表示当前点是哪个类型(某子树极大,自己极大,都不怎么大)

#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 4e5 + 10;
struct node {
node *ch[2], *fa;
LL tp, a, tot, siz;//维护子树a之和,siz记录虚子树的东西,tot是总共的和
node(LL tp = 0, LL a = 0, LL tot = 0, LL siz = 0): tp(tp), a(a), tot(tot), siz(siz) {}
void upd() {
tot = siz + a;
if(ch[0]) tot += ch[0]->tot;
if(ch[1]) tot += ch[1]->tot;
}
bool ntr() { return fa && (fa->ch[0] == this || fa->ch[1] == this); }
bool isr() { return fa->ch[1] == this; }
}pool[maxn];
struct EDGE {
int to;
EDGE *nxt;
EDGE(int to = 0, EDGE *nxt = NULL): to(to), nxt(nxt) {}
}*head[maxn];
void add(int from, int to) {
head[from] = new EDGE(to, head[from]);
}
void rot(node *x) {
node *y = x->fa, *z = y->fa;
bool k = x->isr(); node *w = x->ch[!k];
if(y->ntr()) z->ch[y->isr()] = x;
(x->ch[!k] = y)->ch[k] = w;
(y->fa = x)->fa = z;
if(w) w->fa = y;
y->upd(), x->upd();
}
void splay(node *o) {
while(o->ntr()) {
if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
rot(o);
}
}
int n, m;
LL ans;
void dfs(node *x, node *fa) { //初始的ans
node *pos = x;
LL max = x->a;
for(EDGE *i = head[x - pool]; i; i = i->nxt) {
node *y = pool + i->to;
if(y == fa) continue;
dfs(y, x);
y->fa = x;
x->siz += y->tot; //刚开始都是虚边
if(max < y->tot) max = y->tot, pos = y; //找到max用来DP
}
if(max << 1 > (x->tot = x->siz + x->a)) { //取后面
ans += (x->tot - max) << 1;
if(x != pos) x->siz -= (x->ch[1] = pos)->tot; //子树大,连实边
else x->tp = 1; //自己大
}
else x->tp = 2, ans += x->tot - 1; //取前面
}
void access(node *x, LL w) {
for(node *y = NULL; x; x = (y = x)->fa) {
splay(x);
LL sum = x->tot - (x->ch[0]? x->ch[0]->tot : 0); //sum是原来子树的a之和(子树肯定比自己深度大,所以减去左子树)
//减去原来的贡献,通过type来搞
if(x->tp < 2) {
if(x->tp) ans -= (sum - x->a) << 1;
else ans -= (sum - (x->ch[1]? x->ch[1]->tot : 0)) << 1;
}
else ans -= sum - 1;
sum += w, x->tot += w; //进行修改
if(y) x->siz += w;
else x->a += w;
if(y && (y->tot << 1) > sum) { //是否应该改变实边所向
if(x->ch[1]) x->siz += x->ch[1]->tot;
x->siz -= (x->ch[1] = y)->tot;
}
if(x->ch[1] && (x->ch[1]->tot << 1) > sum) { //统计新贡献
x->tp = 0;
ans += (sum - x->ch[1]->tot) << 1;
}
else {
if(x->ch[1]) x->siz += x->ch[1]->tot, x->ch[1] = NULL;
if((x->a << 1) > sum) x->tp = 1, ans += (sum - x->a) << 1;
else x->tp = 2, ans += (sum - 1), x->ch[1] = 0;
}
}
} int main() {
n = in(), m = in();
int x, y;
for(int i = 1; i <= n; i++) pool[i].a = in();
for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y), add(y, x);
dfs(pool + 1, pool), printf("%lld\n", ans);
while(m --> 0) x = in(), y = in(), access(pool + x, y), printf("%lld\n", ans);
return 0;
}

P4338 [ZJOI2018]历史 LCT+树形DP的更多相关文章

  1. 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)

    洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...

  2. Luogu4338 ZJOI2018 历史 LCT、贪心

    传送门 题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access ...

  3. 洛谷.4383.[八省联考2018]林克卡特树lct(树形DP 带权二分)

    题目链接 \(Description\) 给定一棵边带权的树.求删掉K条边.再连上K条权为0的边后,新树的最大直径. \(n,K\leq3\times10^5\). \(Solution\) 题目可以 ...

  4. 洛谷 4383 [八省联考2018]林克卡特树lct——树形DP+带权二分

    题目:https://www.luogu.org/problemnew/show/P4383 关于带权二分:https://www.cnblogs.com/flashhu/p/9480669.html ...

  5. P4383 [八省联考2018]林克卡特树lct 树形DP+凸优化/带权二分

    $ \color{#0066ff}{ 题目描述 }$ 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的 ...

  6. 【BZOJ5212】[ZJOI2018] 历史(LCT大黑题)

    点此看题面 大致题意: 给定一棵树每个节点\(Access\)的次数,求最大虚实链切换次数,带修改. 什么是\(Access\)? 推荐你先去学一学\(LCT\)吧. 初始化(不带修改的做法) 首先考 ...

  7. 模拟赛:树和森林(lct.cpp) (树形DP,换根DP好题)

    题面 题解 先解决第一个子问题吧,它才是难点 Subtask_1 我们可以先用一个简单的树形DP处理出每棵树内部的dis和,记为dp0[i], 然后再用一个换根的树形DP处理出每棵树内点 i 到树内每 ...

  8. 【BZOJ5212】[ZJOI2018]历史(Link-Cut Tree)

    [BZOJ5212][ZJOI2018]历史(Link-Cut Tree) 题面 洛谷 BZOJ 题解 显然实际上就是给定了一棵树和每个点被\(access\)的次数,求解轻重链切换的最大次数. 先考 ...

  9. [ZJOI2018]历史

    [ZJOI2018]历史 最大化access轻重链的切换次数 考虑一个点的贡献,即它交换重儿子的次数 发现这个次数只和它自己ai以及每个儿子的子树次数和有关. 一个关键的事实是: 我们可以自上而下进行 ...

随机推荐

  1. mybatis 学习六 MyBatis主配置文件

    在定义sqlSessionFactory时需要指定MyBatis主配置文件: <bean id="sqlSessionFactory" class="org.myb ...

  2. android 定位的几种方式介绍

    [地理位置] android 定位的几种方式介绍 开发中对于地图及地理位置的定位是我们经常要用地,地图功能的使用使得我们应用功能更加完善,下面 www.androidkaifa.com 总结了一下网络 ...

  3. highcharts图表显示鼠标选择的Y轴提示线

    tooltip: { shared: true, crosshairs: [true, false] },

  4. dubbo错误排查之No provider available for the service

    今天搞的一个dubbo服务,暴漏出来了,但是consumer端启动就报这个错,排查过程记录一下 一.启动zkCli 利用命令查看 ls / ls /dubbo 继续查看 ls /dubbo/com.w ...

  5. JMS编程模型

    (1) ConnectionFactory创建Connection对象的工厂,针对两种不同的jms消息模型,分别有QueueConnectionFactory和TopicConnectionFacto ...

  6. java中用正则表达式判断中文字符串中是否含有英文或者数字

    public static boolean includingNUM(String str)throws  Exception{ Pattern p  = Pattern.compile(" ...

  7. php学习笔记-php中的比较运算符

    其中比较难懂的是==和=== ==是只比较两个变量的值,不仅仅是用于比较两个数是否相等,还可以比较int和string,不过会先转化string为int类型再比较,值相等则返回true,值不相等则返回 ...

  8. java中字符串的存储

    在java中,不同的字符串赋值方法,其所在的地址可能不同也就导致,两个字符串的值看似相等可是在s1==s2操作时,其结果返回的却是false 例: String s1 = "Programm ...

  9. 95E Lucky Country

    传送门 题目大意 如果一个数中不包含除4和7之外的数字则是幸运数.有n个岛屿,通过双向道路连接.这些岛屿被分为几个地区.每个岛属于恰好一个区域,同一区域中的任何两个岛之间存在道路,不同区域的任何两个岛 ...

  10. Android RecycleView

    Android RecyclerView 用来替代传统的ListView 要在Android Studio 中使用RecyclerView 首先要依赖相应的包 右键项目--->Open Modu ...