P4719 【模板】"动态 DP"&动态树分治
题目描述
给定一棵 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\)是轻儿子。
这样我们就可以直接维护转移矩阵,修改一条链了。
转移矩阵:
\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"&动态树分治的更多相关文章
- 洛谷P4719 【模板】"动态 DP"&动态树分治
[模板]"动态 DP"&动态树分治 第一道动态\(DP\)的题,只会用树剖来做,全局平衡二叉树什么的就以后再学吧 所谓动态\(DP\),就是在原本的\(DP\)求解的问题上 ...
- luoguP4719 【模板】动态 DP 线段树+树链剖分+矩阵乘法+动态DP
题目描述 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y,表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入输出格式 输入格式: 第一行,n,m分 ...
- [动态dp]线段树维护转移矩阵
背景:czy上课讲了新知识,从未见到过,总结一下. 所谓动态dp,是在动态规划的基础上,需要维护一些修改操作的算法. 这类题目分为如下三个步骤:(都是对于常系数齐次递推问题) 1先不考虑修改,不考虑区 ...
- bzoj 5294: [Bjoi2018]二进制【动态dp+线段树】
不太清楚是不是动态dp--? 这个维护其实和最大连续子段差不多,维护l[x][y],r[x][y],m[x][y]分别表示包含左儿子的01个数为(x,y)的区间个数,包含右儿子的01个数为(x,y)的 ...
- Solution -「洛谷 P4719」「模板」"动态 DP" & 动态树分治
\(\mathcal{Description}\) Link. 给定一棵 \(n\) 个结点的带权树,\(m\) 次单点点权修改,求出每次修改后的带权最大独立集. \(n,m\le10^5 ...
- 2019.01.04 洛谷P4719 【模板】动态dp(链分治+ddp)
传送门 ddpddpddp模板题. 题意简述:给你一棵树,支持修改一个点,维护整棵树的最大带权独立集. 思路: 我们考虑如果没有修改怎么做. 貌似就是一个sbsbsb树形dpdpdp,fi,0f_{i ...
- 洛谷P4719 动态DP —— 动态DP(树剖+矩乘)
题目:https://www.luogu.org/problemnew/show/P4719 感觉这篇博客写得挺好:https://blog.csdn.net/litble/article/detai ...
- 【洛谷P4719】动态dp 动态dp模板
题目大意:给你一颗$n$个点的树,点有点权,有$m$次操作,每次操作给定$x$,$y$,表示修改点$x$的权值为$y$. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 数据范围:$n,m≤ ...
- SP1716 GSS3 - Can you answer these queries III - 动态dp,线段树
GSS3 Description 动态维护最大子段和,支持单点修改. Solution 设 \(f[i]\) 表示以 \(i\) 为结尾的最大子段和, \(g[i]\) 表示 \(1 \sim i\) ...
随机推荐
- scala---lazy
scala中用lazy定义的变量叫做惰性变量,会实现延迟加载.惰性变量只能是不可变的变量.并且只有在调用惰性变量的时候才会被初始化. class Test1 { } object Test1 { de ...
- 使用Flask开发简单接口(4)--借助Redis实现token验证
前言 在之前我们已开发了几个接口,并且可以正常使用,那么今天我们将继续完善一下.我们注意到之前的接口,都是不需要进行任何验证就可以使用的,其实我们可以使用 token ,比如设置在修改或删除用户信息的 ...
- 《国际化Web项目测试:记第一次兼职测试的经历(一)》
疫情期间我一直在家远程办公,无意间接到了个做测试兼职的机会.在不耽搁本职工作的情况下,我从今年五月份开启了主职和副职的并行的状态.这种项目经历对于我来说算是一次全新的体验,当然也真是累的够呛.到目前为 ...
- 简单的 Nginx+Tomcat 配置负载均衡集群
简单 Nginx+Tomcat 配置负载均衡集群 前期准备 解压两个tomcat,修改端口号 server1:8081 server:8082 同时启动 nginx官网下载解压版nginx 创建一个简 ...
- JS 仿京东放大镜
css代码 body{ margin:; } .box { width: 1210px; position: relative; background-color: pink; margin: 0 a ...
- 1、迭代器 Iterator模式 一个一个遍历 行为型设计模式
1.Iterator模式 迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器(container,例如链表或者阵列)上遍访的接口,设计人员无需关心容器的内容. I ...
- 网络协议: TCP/IP 和UDP/IP
网络协议: TCP/IP 和UDP/IP TCP/IP TCP/IP(Transmission Control Protocol/Internet Protocol)是一种可靠的网络数据传输控制协议. ...
- SQL性能优化-查询条件与字段分开执行,union代替in与or,存储过程代替union
PS:概要.背景.结语都是日常“装X”,可以跳过直接看优化历程 环境:SQL Server 2008 R2.阿里云RDS:辅助工具:SQL 审计 概要 一个订单列表分页查询功能,单从SQL性能来讲,从 ...
- C# ASP 动态添加Html Table行
用JS放法实现以下效果: 前端文件Questionnaire23.aspx: <%@ Page Title="题目" Language="C#" Mast ...
- 图数据库HugeGraph源码解读 (1) —— 入门介绍
HugeGraph介绍 以下引自官方文档: HugeGraph是一款易用.高效.通用的开源图数据库系统(Graph Database,GitHub项目地址), 实现了Apache TinkerPop3 ...