传送门

Solution

  • \(f_{i,0}\) 表示以i节点为根的子树内,不选i号节点的最大独立集
  • \(f_{i,1}\)表示以i节点为根的子树内,选i号节点的最大独立集
  • \(g_{i,0}\) 表示以i节点为根的子树内,不选i号节点,不算它的重节点子树的最大独立集
  • \(g_{i,1}\) 表示以i节点为根的子树内,选i号节点,不算它的重节点子树的最大独立集

把矩阵乘法的加法改成max,乘法改成加法,仍然符合结合律。

先进行树链剖分,对于同一条链上的点,我们的更新可以写成如下的矩阵乘法:

\[\ \ \ \ \ \ \ \ \ \left[ \begin{matrix} g_{i,0} & g_{i,0} \\ g_{i,1} & 0 \end{matrix} \right] \left[ \begin{matrix} f_{i-1,0}\\ f_{i-1,1} \end{matrix} \right] = \left[ \begin{matrix} f_{i,0}\\ f_{i,1} \end{matrix} \right]
\]

矩阵的右下角是0,但是显然并不影响正确性

用线段树维护区间乘积,每次修改在当前节点到根的路径上进行。

  1. 先单点修改当前点的g值
  2. 用区间乘法算出top节点的f值
  3. 更新top节点的父亲节点的g值
  4. 重复以上操作,直至到根节点
  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define max(a,b) ((a)>(b)?(a):(b))
  4. #define min(a,b) ((a)<(b)?(a):(b))
  5. inline int read()
  6. {
  7. int x=0,f=1;char ch=getchar();
  8. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  9. while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
  10. return x*f;
  11. }
  12. #define MN 100005
  13. int n,m,v[MN];
  14. struct edge{int to,nex;}e[MN<<1];
  15. int hr[MN],en;
  16. inline void ins(int f,int t)
  17. {
  18. e[++en]=(edge){t,hr[f]};hr[f]=en;
  19. e[++en]=(edge){f,hr[t]};hr[t]=en;
  20. }
  21. int mx[MN],siz[MN],top[MN],fa[MN];
  22. void dfs1(int x,int f)
  23. {
  24. siz[x]=1;fa[x]=f;register int i;
  25. for(i=hr[x];i;i=e[i].nex)if(f^e[i].to)
  26. {
  27. dfs1(e[i].to,x);siz[x]+=siz[e[i].to];
  28. if(siz[e[i].to]>siz[mx[x]]) mx[x]=e[i].to;
  29. }
  30. }
  31. void dfs2(int x,int f,int tp)
  32. {
  33. top[x]=tp;if(mx[x]) dfs2(mx[x],x,tp);
  34. register int i;
  35. for(i=hr[x];i;i=e[i].nex)if((e[i].to^f)&&(e[i].to^mx[x])) dfs2(e[i].to,x,e[i].to);
  36. }
  37. struct matrix
  38. {
  39. ll a[2][2];
  40. matrix(){memset(a,0,sizeof a);}
  41. matrix operator * (const matrix &b) const
  42. {
  43. register matrix c;register int i,j,k;
  44. for(i=0;i<2;++i)for(j=0;j<2;j++)for(k=0;k<2;++k)
  45. c.a[i][j]=max(c.a[i][j],b.a[i][k]+a[k][j]);
  46. return c;
  47. }
  48. }t[MN<<2],Ans;
  49. ll g[MN][2],f[MN][2];
  50. int pos[MN],id[MN],dind,st[MN];
  51. void init(int x,int F)
  52. {
  53. register int i;g[x][1]=(ll)v[x];
  54. for(i=hr[x];i;i=e[i].nex)
  55. if((e[i].to^F)&&(e[i].to^mx[x]))
  56. {
  57. init(e[i].to,x);
  58. g[x][0]+=max(f[e[i].to][0],f[e[i].to][1]);
  59. g[x][1]+=f[e[i].to][0];
  60. }
  61. f[x][0]=g[x][0];f[x][1]=g[x][1];
  62. if(mx[x])
  63. {
  64. init(mx[x],x);
  65. f[x][0]+=max(f[mx[x]][0],f[mx[x]][1]);
  66. f[x][1]+=f[mx[x]][0];
  67. }
  68. pos[x]=++dind;id[dind]=x;
  69. if(st[top[x]]==0) st[top[x]]=dind;
  70. }
  71. #define mid (l+r>>1)
  72. void build(int k,int l,int r)
  73. {
  74. if(l==r)
  75. {
  76. t[k].a[0][0]=t[k].a[0][1]=g[id[l]][0];
  77. t[k].a[1][0]=g[id[l]][1];t[k].a[1][1]=0ll;
  78. return;
  79. }
  80. build(k<<1,l,mid);build(k<<1|1,mid+1,r);
  81. t[k]=t[k<<1]*t[k<<1|1];
  82. }
  83. void Modify(int k,int l,int r,int x)
  84. {
  85. if(l==r)
  86. {
  87. t[k].a[0][0]=t[k].a[0][1]=g[id[l]][0];
  88. t[k].a[1][0]=g[id[l]][1];t[k].a[1][1]=0ll;
  89. return;
  90. }
  91. if(x<=mid) Modify(k<<1,l,mid,x);
  92. else Modify(k<<1|1,mid+1,r,x);
  93. t[k]=t[k<<1]*t[k<<1|1];
  94. }
  95. matrix query(int k,int l,int r,int a,int b)
  96. {
  97. if(l==a&&r==b) return t[k];
  98. if(b<=mid) return query(k<<1,l,mid,a,b);
  99. if(a>mid) return query(k<<1|1,mid+1,r,a,b);
  100. return query(k<<1,l,mid,a,mid)*query(k<<1|1,mid+1,r,mid+1,b);
  101. }
  102. ll Que()
  103. {
  104. Ans=query(1,1,n,st[1],pos[1]);
  105. return max(Ans.a[0][0],Ans.a[1][0]);
  106. }
  107. inline void change(int x,ll add)
  108. {
  109. g[x][1]+=add;
  110. while(x!=0){
  111. Modify(1,1,n,pos[x]);
  112. matrix tmp=query(1,1,n,st[top[x]],pos[top[x]]);
  113. ll f0=tmp.a[0][0],f1=tmp.a[1][0];
  114. if(top[x]!=1){
  115. g[fa[top[x]]][1]+=f0-f[top[x]][0];
  116. g[fa[top[x]]][0]+=max(f1,f0)-max(f[top[x]][0],f[top[x]][1]);
  117. }
  118. f[top[x]][0]=f0;f[top[x]][1]=f1;
  119. x=fa[top[x]];
  120. }
  121. }
  122. int main()
  123. {
  124. n=read(),m=read();
  125. register int i,j;
  126. for(i=1;i<=n;i++) v[i]=read();
  127. for(i=1;i<n;i++) ins(read(),read());
  128. dfs1(1,0);dfs2(1,0,1);init(1,0);build(1,1,n);
  129. while(m--)
  130. {
  131. i=read();j=read();
  132. change(i,j-v[i]);v[i]=j;
  133. printf("%lld\n",Que());
  134. }
  135. return 0;
  136. }

Blog来自PaperCloud,未经允许,请勿转载,TKS!

[luogu 4719][模板]动态dp的更多相关文章

  1. [模板] 动态dp

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

  2. 【洛谷4719】 动态dp(树链剖分,dp,矩阵乘法)

    前言 其实我只是为了过掉模板而写的ddp,实际应用被吊着锤 Solution 并不想写详细的过程 一句话过程:将子树中轻儿子的贡献挂到这个点上面来 详细版:(引用yyb) 总结一下的话,大致的过程是这 ...

  3. Luogu P4643 【模板】动态dp

    题目链接 Luogu P4643 题解 猫锟在WC2018讲的黑科技--动态DP,就是一个画风正常的DP问题再加上一个动态修改操作,就像这道题一样.(这道题也是PPT中的例题) 动态DP的一个套路是把 ...

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

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

  5. 洛谷4719 【模板】动态dp 学习笔记(ddp 动态dp)

    qwq大概是混乱的一个题. 首先,还是从一个比较基础的想法开始想起. 如果每次暴力修改的话,那么每次就可以暴力树形dp 令\(dp[x][0/1]\)表示\(x\)的子树中,是否选择\(x\)这个点的 ...

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

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

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

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

  8. 【模板】动态 DP

    luogu传送门. 最近学了一下动态dp,感觉没有想象的难. 动态DP simple的DP是这样的: 给棵树,每个点给个权值,求一下最大权独立集. 动态DP是这样的: 给棵树,每个点给个权值还到处改, ...

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

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

随机推荐

  1. 第三方dll签名

    1.打开vs Tools下的工具命令 2.生成随机密钥对C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC>sn -k NonSignL ...

  2. VS2017 配置 boost_1_70

    1. 下载与安装 1.1 安装方法1 (1) 下载 https://www.boost.org/ 或者使用 https://sourceforge.net/projects/boost/files/b ...

  3. Jmeter学习笔记(九)——响应断言

    Jmeter中又一个元件叫断言,用于检查测试中得到的响应数据等是否符合预期.断言又13种,目前在使用过程中使用到的是响应断言. 有时候请求成功了并不代表测试通过,还要看影响返回的内容是否符合预期的结果 ...

  4. Python学习日记(六) 浅深copy

    浅深copy即完全复制一份和部分复制一份 浅深copy在列表数据量较大时不建议使用,比较消耗内存资源 1.赋值运算 l1 = [1,'s',[1,2,3]] l2 = l1 print(id(l1), ...

  5. python中的debug

    python中有很多的debug方法,大部分新人忽略了Python debugger(pdb)的重要性. 1.命令行运行 在终端中输入命令行   python -m pdb helloword.py ...

  6. 在Linux主机使用命令行批量删除harbor镜像

     在Linux主机使用命令行批量删除harbor镜像 脚本使用说明: 此脚本不是万能脚本,根据自身环境要调整很多 能用harbor的域名就不要用IP 脚本前半部分可以套用,后半部分需一步一步试错,结合 ...

  7. c# BufferedStream 类

  8. 自动网页截图并指定元素位置裁剪图片并保存到excel表格

    # coding=utf-8 import os import time from selenium import webdriver from selenium.webdriver.chrome.o ...

  9. 一个97年测试妹纸的成长经历,转正直接涨薪2K

    这篇文章,涉及测试团队管理.测试流程建设.测试从业者能力成长.优秀测试从业者的状态.以及同样是两年的Tester,为何他人如此优秀 . 一切的一切,都是有原因的 . 期望这篇文章,对关注「简尚」公号的 ...

  10. NTP时间服务器+DHCP服务器的搭建

    一.构建NTP时间服务器 ntp服务器监控端口UDP:123 安装ntp和ntpdate 命令:yum  -y install ntp netdate 修改配置文件/etc/ntp.conf 启动nt ...