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

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

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

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

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

第一行,\(n,m\)分别代表点数和操作数。

第二行,\(V_1,V_2,...,V_n\),代表\(n\)个点的权值。

接下来\(n-1\)行,\(x,y\),描述这棵树的\(n-1\)条边。

接下来\(m\)行,\(x,y\),修改点xx的权值为\(y\)。

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

对于每个操作输出一行一个整数,代表这次操作后的树上最大权独立集。

保证答案在int范围内

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

  1. 10 10
  2. -11 80 -99 -76 56 38 92 -51 -34 47
  3. 2 1
  4. 3 1
  5. 4 3
  6. 5 2
  7. 6 2
  8. 7 1
  9. 8 2
  10. 9 4
  11. 10 7
  12. 9 -44
  13. 2 -17
  14. 2 98
  15. 7 -58
  16. 8 48
  17. 3 99
  18. 8 -61
  19. 9 76
  20. 9 14
  21. 10 93

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

  1. 186
  2. 186
  3. 190
  4. 145
  5. 189
  6. 288
  7. 244
  8. 320
  9. 258
  10. 304

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

对于30%的数据,\(1\le n,m\le 10\)

对于60%的数据,\(1\le n,m\le 1000\)

对于100%的数据,\(1\le n,m\le 10^5\)

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

动态DP就是带修改的DP

首先,考虑不带修改的DP

对于本题

设\(f[i][0]\)为以i为根子树不选i的ans,\(f[i][1]\)为以i为根子树选i的ans

转移

\(f[x][0]=\sum max(f[son][0],f[son][1])\)

\(f[x][1] = \sum f[son][0]+val[x]\)

我们进行树链剖分

定义\(g[x][0/1]\)为以x为根子树,只考虑轻儿子的ans

这个可以跟f一块求出,只有当儿子是轻儿子的时候才用儿子的f转移到当前的g

那么我们改写DP式子

\(f[x][0]=g[x][0]+max(f[son][0],f[son][1])\)

\(f[x][1]=g[x][1]+f[son][0]\)

然后每个点,我们维护一个矩阵,考虑转移

\(\left\{\begin{matrix} f[son][0] \\ f[son][1] \end{matrix}\right\} \to \left\{\begin{matrix} f[x][0] \\ f[x][1] \end{matrix}\right\}\)

由转移式子得出,需要取max和+

所以我们定义 * 为 + , + 为取max

不难推出转移矩阵

\(\left\{\begin{matrix} g[x][0]& g[x][0]\\ g[x][1] & -inf \end{matrix}\right\}\)

因此\(\left\{\begin{matrix} g[x][0]& g[x][0]\\ g[x][1] & -inf \end{matrix}\right\} * \left\{\begin{matrix} f[son][0] \\ f[son][1] \end{matrix}\right\} = \left\{\begin{matrix} f[x][0] \\ f[x][1] \end{matrix}\right\}\)

可以发现,叶子节点的f和g是一样的,而且一条重链,必以叶子节点结束

所以我们按照树链剖分,用线段树维护矩阵的积,每条重链记录一下这条链的链尾的dfn

这样ans=1所在重链的矩阵的积的其中左面两个值的max

考虑修改

对于点x

可以发现,如果x是重儿子,那么所在重链的转移矩阵是毫无影响的

因为矩阵中都是g,而g是轻儿子的ans

那么我们每次只需修改一个点,然后跳到链顶,这时候,链顶的fa的矩阵需要改变

可以在改变前求出链顶矩阵,改变后求出链顶矩阵

然后根据DP式子,找到矩阵的变化量\(\Delta\),其实就是减去原来这个儿子的贡献在加上新的贡献就行了

因为本题的DP都是取\(\sum\),所以直接加减就行了

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. LL in() {
  4. char ch; LL x = 0, f = 1;
  5. while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
  6. for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
  7. return x * f;
  8. }
  9. const int maxn = 1e5 + 100;
  10. const int inf = 0x3f3f3f3f;
  11. struct Matrix {
  12. int a[2][2];
  13. Matrix(int n = 0, int m = 0, int p = 0, int q = 0) {
  14. a[0][0] = n;
  15. a[0][1] = m;
  16. a[1][0] = p;
  17. a[1][1] = q;
  18. }
  19. friend Matrix operator * (const Matrix &c, const Matrix &d) {
  20. Matrix t(-inf, -inf, -inf, -inf);
  21. for(int i = 0; i <= 1; i++)
  22. for(int j = 0; j <= 1; j++)
  23. for(int k = 0; k <= 1; k++)
  24. t.a[i][j] = std::max(t.a[i][j], c.a[i][k] + d.a[k][j]);
  25. return t;
  26. }
  27. friend Matrix operator + (const Matrix &c, const Matrix &d) {
  28. return Matrix(c.a[0][0] + d.a[0][0], c.a[0][1] + d.a[0][1], c.a[1][0] + d.a[1][0], c.a[1][1] + d.a[1][1]);
  29. }
  30. friend Matrix operator - (const Matrix &c, const Matrix &d) {
  31. return Matrix(c.a[0][0] - d.a[0][0], c.a[0][1] - d.a[0][1], c.a[1][0] - d.a[1][0], c.a[1][1] - d.a[1][1]);
  32. }
  33. }val[maxn];
  34. struct Tree {
  35. int l, r;
  36. Tree *ch[2];
  37. Matrix val;
  38. Tree(int l = 0, int r = 0, Matrix c = Matrix(0, 0, 0, 0)): l(l), r(r), val(c) {
  39. ch[0] = ch[1] = NULL;
  40. }
  41. void *operator new (size_t) {
  42. static Tree *S = NULL, *T = NULL;
  43. return (S == T) && (T = (S = new Tree[1024]) + 1024), S++;
  44. }
  45. void upd() {
  46. val = ch[0]->val * ch[1]->val;
  47. }
  48. };
  49. Tree *root;
  50. struct node {
  51. int to;
  52. node *nxt;
  53. node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
  54. void *operator new (size_t) {
  55. static node *S = NULL, *T = NULL;
  56. return (S == T) && (T = (S = new node[1024]) + 1024), S++;
  57. }
  58. };
  59. node *head[maxn];
  60. int son[maxn], dep[maxn], siz[maxn], fa[maxn], n, m, c[maxn];
  61. int top[maxn], dfn[maxn], redfn[maxn], e[maxn];
  62. int f[maxn][2], g[maxn][2], cnt;
  63. void add(int from, int to) {
  64. head[from] = new node(to, head[from]);
  65. }
  66. void dfs1(int x, int ft) {
  67. fa[x] = ft;
  68. dep[x] = dep[ft] + 1;
  69. siz[x] = 1;
  70. for(node *i = head[x]; i; i = i->nxt) {
  71. if(i->to == ft) continue;
  72. dfs1(i->to, x);
  73. siz[x] += siz[i->to];
  74. if(!son[x] || siz[i->to] > siz[son[x]]) son[x] = i->to;
  75. }
  76. }
  77. void dfs2(int x, int t) {
  78. top[redfn[dfn[x] = ++cnt] = x] = t;
  79. e[t] = dfn[x];
  80. if(son[x]) dfs2(son[x], t);
  81. for(node *i = head[x]; i; i = i->nxt)
  82. if(!dfn[i->to]) dfs2(i->to, i->to);
  83. }
  84. void dfs3(int x, int ft) {
  85. f[x][1] = g[x][1] = c[x];
  86. for(node *i = head[x]; i; i = i->nxt) {
  87. if(i->to == ft) continue;
  88. dfs3(i->to, x);
  89. f[x][1] += f[i->to][0];
  90. f[x][0] += std::max(f[i->to][0], f[i->to][1]);
  91. if(i->to != son[x]) {
  92. g[x][1] += f[i->to][0];
  93. g[x][0] += std::max(f[i->to][0], f[i->to][1]);
  94. }
  95. }
  96. val[x] = Matrix(g[x][0], g[x][0], g[x][1], -inf);
  97. }
  98. void build(Tree *&o, int l, int r) {
  99. o = new Tree(l, r, Matrix(0, 0, 0, 0));
  100. if(l == r) return (void)(o->val = val[redfn[l]]);
  101. int mid = (l + r) >> 1;
  102. build(o->ch[0], l, mid);
  103. build(o->ch[1], mid + 1, r);
  104. o->upd();
  105. }
  106. Matrix query(Tree *o, int l, int r) {
  107. if(o->r < l || o->l > r) return Matrix(0, -inf, -inf, 0);
  108. if(l <= o->l && o->r <= r) return o->val;
  109. return query(o->ch[0], l, r) * query(o->ch[1], l, r);
  110. }
  111. void change(Tree *o, int pos, Matrix v) {
  112. if(o->r < pos || o->l > pos) return;
  113. if(o->l == o->r) return (void)(o->val = v);
  114. change(o->ch[0], pos, v), change(o->ch[1], pos, v);
  115. o->upd();
  116. }
  117. Matrix query(int x) {
  118. return query(root, dfn[x], e[top[x]]);
  119. }
  120. void change(int x, int v) {
  121. val[x] = val[x] + Matrix(0, 0, -c[x] + v, 0);
  122. c[x] = v;
  123. int fx = top[x], ffx = fa[fx];
  124. while(1) {
  125. Matrix Old = query(fx);
  126. change(root, dfn[x], val[x]);
  127. if(!ffx) return;
  128. Matrix New = query(fx);
  129. val[ffx].a[0][0] += std::max(New.a[0][0], New.a[1][0]) - std::max(Old.a[0][0], Old.a[1][0]);
  130. val[ffx].a[0][1] = val[ffx].a[0][0];
  131. val[ffx].a[1][0] += New.a[0][0] - Old.a[0][0];
  132. x = ffx;
  133. fx = top[x];
  134. ffx = fa[fx];
  135. }
  136. }
  137. int main() {
  138. n = in(), m = in();
  139. for(int i = 1; i <= n; i++) c[i] = in();
  140. int x, y;
  141. for(int i = 1; i < n; i++) {
  142. x = in(), y = in();
  143. add(x, y), add(y, x);
  144. }
  145. dfs1(1, 0);
  146. dfs2(1, 1);
  147. dfs3(1, 0);
  148. build(root, 1, n);
  149. while(m --> 0) {
  150. x = in(), y = in();
  151. change(x, y);
  152. Matrix ans = query(1);
  153. printf("%d\n", std::max(ans.a[0][0], ans.a[1][0]));
  154. }
  155. return 0;
  156. }

P4719 【模板】动态dp的更多相关文章

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

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

  2. [模板] 动态dp

    用途 对于某些树形dp(目前只会树上最大权独立集或者类似的),动态地修改点权,并询问修改后的dp值 做法(树剖版) 以最大权独立集为例 设$f[x][0/1]$表示x选不选,这棵子树的最大权独立集大小 ...

  3. [luogu 4719][模板]动态dp

    传送门 Solution \(f_{i,0}\) 表示以i节点为根的子树内,不选i号节点的最大独立集 \(f_{i,1}\)表示以i节点为根的子树内,选i号节点的最大独立集 \(g_{i,0}\) 表 ...

  4. 洛谷4719 【模板】动态dp

    题目:https://www.luogu.org/problemnew/show/P4719 关于动态DP似乎有猫锟的WC2018论文,但找不见:还是算了. http://immortalco.blo ...

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

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

  6. 洛谷P4719 【模板】动态dp(ddp LCT)

    题意 题目链接 Sol 动态dp板子题.有些细节还没搞懂,待我研究明白后再补题解... #include<bits/stdc++.h> #define LL long long using ...

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

    是动态dp的板子 大致思想就是用g[u]来表示不包含重链转移的dp值,然后用线段树维护重链,这样线段树的根就相当于这条重链的top的真实dp值 每次修改的时候,修改x点会影响到x到根的真实dp值,但是 ...

  8. P4719 【模板】"动态 DP"&动态树分治

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

  9. LG4719 【模板】动态dp 及 LG4751 动态dp【加强版】

    题意 题目描述 给定一棵\(n\)个点的树,点带点权. 有\(m\)次操作,每次操作给定\(x,y\),表示修改点\(x\)的权值为\(y\). 你需要在每次操作之后求出这棵树的最大权独立集的权值大小 ...

随机推荐

  1. [Chapter 3 Process]Practice 3.1 相关知识:进程创建、fork函数

    3.1 Using the program shown in the Figure3.30, explain what the output will be at LINE A 答案:LINE A 处 ...

  2. 11-24网页基础--Js基础语法

    1.运算符 比较运算符(7种):==/===/!=/>/</<=/>= ===(全等于) 2.字符串substring的用法 3.练习题:累加求和(运用Js的方法) 4.进制转 ...

  3. 1107SQLserver基础--语句、存储过程

    [随堂练习]--查询‘李数’老师教的数学成绩大于80分的学生的信息, 并且人数大于3的话,输出达标:否则输出不达标. 存储过程 --带参数的程序代码块---代表执行命令存储在数据库中,存储代码,没有调 ...

  4. leetcode628

    这道题十分不容易啊,做到半夜. class Solution { public: static int cmp628(int a, int b) { return a > b; } static ...

  5. oracle --(一)数据块(data Block)

    基本关系:数据库---表空间---数据段---分区---数据块 数据块(data Block)一.数据块Block是Oracle存储数据信息的最小单位.这里说的是Oracle环境下的最小单位.Orac ...

  6. 使用ServerSocket建立聊天服务器(二)

    -------------siwuxie095                         工程名:TestMyServerSocket 包名:com.siwuxie095.socket 类名:M ...

  7. sql如何选取两个数据表中的值

    一.直接在要选择的数据前面加上数据表的名字就行了 SELECT po.OrderID, p.LastName, p.FirstName FROM Persons AS p, Product_Order ...

  8. SQl Server Tsql基本编程 ,循环语句 ,存储过程

    一些比较重要但是不一定经常用的 句子 Tsql定义变量 declare @a int :  定义的变量前面必须用@,数据类型是SQL里的数据类型,执行的时候要把需要的有关联的代码一起执行,单独执行一条 ...

  9. 关于c#里的集合的,结构体,枚举的定义,解释与应用

    那么先写一下 集合 . 集合和数组很相似,数组里的类型是必须同一类型,固定长度.然而集合里的可以是不同类型,不固定长度的.所以集合运用的灵活度要更高一些. 要使用集合,必须先引用命名空间:using ...

  10. IDEA java 代码格式化统一

    Intellij idea 安装格式化插件 ECLIPSE CODE FORMATTER:1,安装插件:网络安装:选择Setting =>Plugins=>Browse repositor ...