题目描述

给定一棵 n 个点的树,点带点权。

有 m 次操作,每次操作给定 x,y,表示修改点 x 的权值为 y。

你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

输入格式

第一行有两个整数,分别表示结点个数 n 和操作个数 m。

第二行有 n 个整数,第 i 个整数表示节点 iii 的权值 ai。

接下来 (n−1) 行,每行两个整数 u,v,表示存在一条连接 u 与 v 的边。

接下来 m 行,每行两个整数 x,y,表示一次操作,修改点 x 的权值为 y。

输出格式

对于每次操作,输出一行一个整数表示答案。

输入输出样例

输入 #1

10 10
-11 80 -99 -76 56 38 92 -51 -34 47
2 1
3 1
4 3
5 2
6 2
7 1
8 2
9 4
10 7
9 -44
2 -17
2 98
7 -58
8 48
3 99
8 -61
9 76
9 14
10 93

输出 #1

186
186
190
145
189
288
244
320
258
304

说明/提示

数据规模与约定

  • 对于 100%1 的数据,保证 1≤n,m≤\(10^5\),1≤u,v,x≤n,\(-10^2 \leq a_i, y \leq 10^2\)。

动态DP板子题。

我们首先可以推出DP方程:\(dpi,0=∑max(dpj,0,dpj,1)\)

​ \(dp_{i, 1} = \sum_{j} dp_{j, 0} + a_i\)

对于每一次修改,我们要\(O(n)\)的改一条链,很不妙,所以用树链剖分,\(O(logn)\)的修改一条链。

我们用\(f_{i, 0/1}\)表示以\(i\)为根的这颗树内,轻儿子的ans。

所以DP方程还可以写成:\(dp_{i, 0} = f_{j, 0} + max(dp_{son, 1}, dp_{son, 0})\)

​ \(dp_{i, 1} = f_{i, 1} + dp_{son, 0}\)

这里\(son\)是轻儿子。

这样我们就可以直接维护转移矩阵,修改一条链了。

转移矩阵:

\[\left[
\begin{array}{}
f[x][0] & f[x][0]\\
f[x][1] & -\infty\\
\end{array}
\right] * \left[
\begin{array}{}
dp[son][0]\\
dp[son][1]\\
\end{array}
\right] = \left[
\begin{array}{}
dp[x][0]\\
dp[x][1]\\
\end{array}
\right]
\]

修改一下矩阵乘法规则,把加法改为取\(max\)

#include <bits/stdc++.h>

#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1) using namespace std; inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
} const int N = 1e5 + 5, inf = 1e9;
int n, m, cnt, tot;
int fa[N], id[N], val[N], end[N], dfn[N], top[N], siz[N], hav[N], head[N];
int f[N][2], dp[N][2];
struct edge { int to, nxt; } e[N << 1];
struct mat {
long long v[2][2];
mat() {
for(int i = 0;i <= 1; i++)
for(int j = 0;j <= 1; j++)
v[i][j] = -inf;
}
} t[N << 2], nod[N]; mat operator * (const mat &a, const mat &b) {
mat c;
for(int i = 0;i <= 1; i++)
for(int j = 0;j <= 1; j++)
for(int k = 0;k <= 1; k++)
c.v[i][j] = max(c.v[i][j], a.v[i][k] + b.v[k][j]);
return c;
} void add(int x, int y) {
e[++cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
} void get_tree(int x, int Fa) {
siz[x] = 1; fa[x] = Fa;
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to; if(y == Fa) continue;
get_tree(y, x);
siz[x] += siz[y];
if(siz[y] > siz[hav[x]]) hav[x] = y;
}
} void get_top(int x, int topic) {
dfn[x] = ++tot; id[tot] = x; top[x] = topic;
if(hav[x] == 0) { end[topic] = tot; return ; }
get_top(hav[x], topic);
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to; if(y == fa[x] || y == hav[x]) continue;
get_top(y, y);
}
} void dfs(int x) {
dp[x][1] = f[x][1] = val[x];
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to; if(y == fa[x]) continue;
dfs(y);
dp[x][0] += max(dp[y][0], dp[y][1]);
dp[x][1] += dp[y][0];
if(y != hav[x]) {
f[x][0] += max(dp[y][0], dp[y][1]);
f[x][1] += dp[y][0];
}
}
} void up(int o) {
t[o] = t[ls(o)] * t[rs(o)];
} void build(int o, int l, int r) {
if(l == r) {
int x = id[l];
t[o].v[0][0] = t[o].v[0][1] = f[x][0];
t[o].v[1][0] = f[x][1];
nod[l] = t[o];
return ;
}
build(ls(o), l, mid); build(rs(o), mid + 1, r);
up(o);
} void change(int o, int l, int r, int x) {
if(l == r) { t[o] = nod[l]; return ; }
if(x <= mid) change(ls(o), l, mid, x);
if(x > mid) change(rs(o), mid + 1, r, x);
up(o);
} mat get_ans(int o, int l, int r, int x, int y) {
if(x <= l && y >= r) return t[o];
if(y <= mid) return get_ans(ls(o), l, mid, x, y);
if(x > mid) return get_ans(rs(o), mid + 1, r, x, y);
return get_ans(ls(o), l, mid, x, y) * get_ans(rs(o), mid + 1, r, x, y);
} void update_tree(int x, int y) {
nod[dfn[x]].v[1][0] += y - val[x];
val[x] = y;
while(x) {
mat Old, New;
int a = x;
x = top[x];
Old = get_ans(1, 1, n, dfn[x], end[x]);
change(1, 1, n, dfn[a]);
New = get_ans(1, 1, n, dfn[x], end[x]);
int tmp = dfn[fa[x]];
nod[tmp].v[0][0] = nod[tmp].v[0][1] += max(New.v[0][0], New.v[1][0]) - max(Old.v[0][0], Old.v[1][0]);
nod[tmp].v[1][0] += New.v[0][0] - 0Old.v[0][0];
x = fa[x];
}
} int main() { n = read(); m = read();
for(int i = 1;i <= n; i++) val[i] = read();
for(int i = 1, x, y;i <= n - 1; i++) {
x = read(); y = read();
add(x, y); add(y, x);
} get_tree(1, 0); get_top(1, 1);
dfs(1); build(1, 1, n); for(int i = 1, x, y;i <= m; i++) {
x = read(); y = read();
update_tree(x, y);
mat ans = get_ans(1, 1, n, dfn[1], end[1]);
printf("%lld\n", max(ans.v[0][0], ans.v[1][0]));
} return 0;
}

P4719 【模板】"动态 DP"&动态树分治的更多相关文章

  1. 洛谷P4719 【模板】"动态 DP"&动态树分治

    [模板]"动态 DP"&动态树分治 第一道动态\(DP\)的题,只会用树剖来做,全局平衡二叉树什么的就以后再学吧 所谓动态\(DP\),就是在原本的\(DP\)求解的问题上 ...

  2. luoguP4719 【模板】动态 DP 线段树+树链剖分+矩阵乘法+动态DP

    题目描述 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y,表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入输出格式 输入格式: 第一行,n,m分 ...

  3. [动态dp]线段树维护转移矩阵

    背景:czy上课讲了新知识,从未见到过,总结一下. 所谓动态dp,是在动态规划的基础上,需要维护一些修改操作的算法. 这类题目分为如下三个步骤:(都是对于常系数齐次递推问题) 1先不考虑修改,不考虑区 ...

  4. bzoj 5294: [Bjoi2018]二进制【动态dp+线段树】

    不太清楚是不是动态dp--? 这个维护其实和最大连续子段差不多,维护l[x][y],r[x][y],m[x][y]分别表示包含左儿子的01个数为(x,y)的区间个数,包含右儿子的01个数为(x,y)的 ...

  5. Solution -「洛谷 P4719」「模板」"动态 DP" & 动态树分治

    \(\mathcal{Description}\)   Link.   给定一棵 \(n\) 个结点的带权树,\(m\) 次单点点权修改,求出每次修改后的带权最大独立集.   \(n,m\le10^5 ...

  6. 2019.01.04 洛谷P4719 【模板】动态dp(链分治+ddp)

    传送门 ddpddpddp模板题. 题意简述:给你一棵树,支持修改一个点,维护整棵树的最大带权独立集. 思路: 我们考虑如果没有修改怎么做. 貌似就是一个sbsbsb树形dpdpdp,fi,0f_{i ...

  7. 洛谷P4719 动态DP —— 动态DP(树剖+矩乘)

    题目:https://www.luogu.org/problemnew/show/P4719 感觉这篇博客写得挺好:https://blog.csdn.net/litble/article/detai ...

  8. 【洛谷P4719】动态dp 动态dp模板

    题目大意:给你一颗$n$个点的树,点有点权,有$m$次操作,每次操作给定$x$,$y$,表示修改点$x$的权值为$y$. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 数据范围:$n,m≤ ...

  9. SP1716 GSS3 - Can you answer these queries III - 动态dp,线段树

    GSS3 Description 动态维护最大子段和,支持单点修改. Solution 设 \(f[i]\) 表示以 \(i\) 为结尾的最大子段和, \(g[i]\) 表示 \(1 \sim i\) ...

随机推荐

  1. 关于tf.cond函数中“正确”与“错误”函数中的普通python语句始终执行的问题

    import tensorflow as tf import numpy as np x = tf.constant(2) y = tf.constant(3) global mask0 mask0 ...

  2. 7月15日考试 题解(链表+状压DP+思维题)

    前言:蒟蒻太弱了,全打的暴力QAQ. --------------------- T1 小Z的求和 题目大意:求$\sum\limits_{i=1}^n \sum\limits_{j=i}^n kth ...

  3. 为什么阿里规定需要在事务注解@Transactional中指定rollbackFor?

    作者:Mint6 来源:http://39sd.cn/53D5D Java阿里巴巴规范提示:方法[edit]需要在Transactional注解指定rollbackFor或者在方法中显示的rollba ...

  4. Springboot中如何引入本地jar包,并通过maven把项目成功打包成jar包部署

    最近尝试引入阿里云的短信验证码,阿里云的core sdk是maven就有的,但是短信相关的jar包却不是放在maven的,所以得引入本地的下载回来的jar包.本地开发直接引入,idea是可以直接跑调用 ...

  5. eureka和feign的使用

    1 eureka和feign的简介(copy来的) eureka:Eureka是Netflix开发的服务发现组件,本身是一个基于REST的服务.Spring Cloud将它集成在其子项目spring- ...

  6. Java基础高级篇 NIO

    nio模型与io模型的对比 netty 是什么 怎么使用

  7. Zabbix5 Frame 嵌套

    Zabbix5 Frame 嵌套 Zabbix 默认不允许嵌套在其他页面上,通过修改配置允许嵌套 找到 Zabbix 下面的 include/defines.inc.php 文件,末尾有一行 defi ...

  8. CentOS7 安装 Nexus

    CentOS7 安装 Nexus 所需软件包 jdk-8u231-linux-x64.tar.gz nexus-3.24.0-02-unix.tar.gz 创建安装目录 mkdir -p /opt/n ...

  9. 手写IOC实现过程

    一.手写ioc前基础知识 1.什么是IOC(Inversion of Control 控制反转)? IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良 ...

  10. .NET Core 微服务—API网关(Ocelot) 教程 [三]

    前言: 前一篇文章<.NET Core 微服务—API网关(Ocelot) 教程 [二]>已经让Ocelot和目录api(Api.Catalog).订单api(Api.Ordering)通 ...