题目:https://www.luogu.org/problemnew/show/P4719

感觉这篇博客写得挺好:https://blog.csdn.net/litble/article/details/81038415

为了动态维护DP值,首先要把它转化成一个容易维护的形式,这道题中DP状态的转移就可以转化成矩阵乘法;

于是要快速算出一个DP值,就可以矩阵连乘,用线段树维护(此时求DP值已经完全变成求区间矩阵乘积了);

可以发现,如果修改一个点的值,影响到的只有它到根的一条链;

所以树剖+线段树维护矩阵,以重链为单位修改,复杂度据说是 23nlog2n ;

注意这里的 ed[x] 不是树剖常用的那个 ed,而是重链底端的 dfn 值,并且只记在 top 上,这样就可以在线段树上从 top 提取出一条重链。

代码如下:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define mid ((l+r)>>1)
  6. #define ls (x<<1)
  7. #define rs (x<<1|1)
  8. using namespace std;
  9. int const xn=1e5+,inf=1e9;
  10. int n,m,a[xn],hd[xn],ct,to[xn<<],nxt[xn<<],dfn[xn],tim,id[xn],top[xn];
  11. int fa[xn],siz[xn],son[xn],f[xn][],ed[xn];
  12. struct N{
  13. int a[][];
  14. N(){a[][]=a[][]=; a[][]=a[][]=-inf;}
  15. N operator * (const N &y) const
  16. {
  17. N ret;
  18. for(int i=;i<;i++)
  19. for(int k=;k<;k++)
  20. for(int j=;j<;j++)
  21. ret.a[i][j]=max(ret.a[i][j],a[i][k]+y.a[k][j]);
  22. return ret;
  23. }
  24. }s[xn],t[xn<<];
  25. int rd()
  26. {
  27. int ret=,f=; char ch=getchar();
  28. while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
  29. while(ch>=''&&ch<='')ret=(ret<<)+(ret<<)+ch-'',ch=getchar();
  30. return f?ret:-ret;
  31. }
  32. void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
  33. int maxx(int a,int b){return a>b?a:b;}
  34. void dfs(int x,int ff)
  35. {
  36. fa[x]=ff; siz[x]=; f[x][]=a[x];
  37. for(int i=hd[x],u;i;i=nxt[i])
  38. {
  39. if((u=to[i])==ff)continue;
  40. dfs(u,x); siz[x]+=siz[u];
  41. if(siz[u]>siz[son[x]])son[x]=u;
  42. f[x][]+=f[u][]; f[x][]+=maxx(f[u][],f[u][]);
  43. }
  44. }
  45. void dfsx(int x)
  46. {
  47. dfn[x]=++tim; id[tim]=x;
  48. if(son[x])top[son[x]]=top[x],dfsx(son[x]);
  49. for(int i=hd[x],u;i;i=nxt[i])
  50. if((u=to[i])!=fa[x]&&u!=son[x])top[u]=u,dfsx(u);
  51. if(!son[x])ed[top[x]]=dfn[x];//!!链底
  52. //ed[x]=tim;
  53. }
  54. void build(int x,int l,int r)
  55. {
  56. if(l==r)
  57. {
  58. int nw=id[l],g0=,g1=a[nw];
  59. for(int i=hd[nw],u;i;i=nxt[i])
  60. if((u=to[i])!=fa[nw]&&u!=son[nw])g0+=maxx(f[u][],f[u][]),g1+=f[u][];
  61. t[x].a[][]=t[x].a[][]=g0; t[x].a[][]=g1;
  62. s[l]=t[x]; return;//s[l]!
  63. }
  64. build(ls,l,mid); build(rs,mid+,r);
  65. t[x]=t[ls]*t[rs];
  66. }
  67. N query(int x,int l,int r,int L,int R)
  68. {
  69. if(l>=L&&r<=R)return t[x];
  70. if(mid>=R)return query(ls,l,mid,L,R);
  71. if(mid<L)return query(rs,mid+,r,L,R);
  72. return query(ls,l,mid,L,R)*query(rs,mid+,r,L,R);
  73. }
  74. N get(int x){return query(,,n,dfn[x],ed[x]);}
  75. void chg(int x,int l,int r,int pos)
  76. {
  77. if(l==r){t[x]=s[l]; return;}//!s[x]!
  78. if(pos<=mid)chg(ls,l,mid,pos);
  79. else chg(rs,mid+,r,pos);
  80. t[x]=t[ls]*t[rs];
  81. }
  82. void work(int x,int ss)
  83. {
  84. s[dfn[x]].a[][]+=ss-a[x]; a[x]=ss;//dfn[x]
  85. N nw,pr;
  86. while()
  87. {
  88. pr=get(top[x]); chg(,,n,dfn[x]); nw=get(top[x]);
  89. x=fa[top[x]]; if(!x)return;
  90. s[dfn[x]].a[][]+=maxx(nw.a[][],nw.a[][])-maxx(pr.a[][],pr.a[][]);
  91. s[dfn[x]].a[][]=s[dfn[x]].a[][];
  92. s[dfn[x]].a[][]+=nw.a[][]-pr.a[][];//dfn[x]
  93. }
  94. }
  95. int main()
  96. {
  97. n=rd(); m=rd();
  98. for(int i=;i<=n;i++)a[i]=rd();
  99. for(int i=,x,y;i<n;i++)x=rd(),y=rd(),add(x,y),add(y,x);
  100. dfs(,); top[]=; dfsx(); build(,,n);
  101. for(int i=,x,y;i<=m;i++)
  102. {
  103. x=rd(); y=rd(); work(x,y); N tmp=get();
  104. printf("%d\n",maxx(tmp.a[][],tmp.a[][]));
  105. }
  106. return ;
  107. }

洛谷P4719 动态DP —— 动态DP(树剖+矩乘)的更多相关文章

  1. 洛谷P1505 [国家集训队]旅游(树剖+线段树)

    传送门 这该死的码农题…… 把每一条边变为它连接的两个点中深度较浅的那一个,然后就是一堆单点修改/路径查询,不讲了 这里就讲一下怎么搞路径取反,只要打一个标记就好了,然后把区间和取反,最大最小值交换然 ...

  2. 洛谷P4719 动态dp

    动态DP其实挺简单一个东西. 把DP值的定义改成去掉重儿子之后的DP值. 重链上的答案就用线段树/lct维护,维护子段/矩阵都可以.其实本质上差不多... 修改的时候在log个线段树上修改.轻儿子所在 ...

  3. 洛谷 P5279 - [ZJOI2019]麻将(dp 套 dp)

    洛谷题面传送门 一道 dp 套 dp 的 immortal tea 首先考虑如何判断一套牌是否已经胡牌了,考虑 \(dp\)​​​​​.我们考虑将所有牌按权值大小从大到小排成一列,那我们设 \(dp_ ...

  4. 洛谷 P3373 【模板】线段树 2

    洛谷 P3373 [模板]线段树 2 洛谷传送门 题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xx 将某区间每一个数加上 xx 求出某区间每一个数的和 输入格式 第一 ...

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

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

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

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

  7. Bzoj3197/洛谷3296 [SDOI2013]刺客信条assassin(树的重心+树Hash+树形DP+KM)

    题面 Bzoj 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...

  8. 洛谷 P2495 [SDOI2011]消耗战(虚树,dp)

    题面 洛谷 题解 虚树+dp 关于虚树 了解一下 具体实现 inline void insert(int x) { if (top == 1) {s[++top] = x; return ;} int ...

  9. bzoj3295 洛谷P3157、1393 动态逆序对——树套树

    题目:bzoj3295 https://www.lydsy.com/JudgeOnline/problem.php?id=3295 洛谷 P3157(同一道题) https://www.luogu.o ...

随机推荐

  1. 彻底搞懂oracle的标量子查询

    oracle标量子查询和自己定义函数有时用起来比較方便,并且开发者也常常使用.数据量小还无所谓.数据量大,往往存在性能问题. 下面測试帮助大家彻底搞懂标量子查询. SQL> create tab ...

  2. AngularJS的Foreach循环示例

    代码下载:https://files.cnblogs.com/files/xiandedanteng/angularJSForeach.rar 代码: <!DOCTYPE HTML PUBLIC ...

  3. leetcode 题解 || Remove Nth Node From End of List 问题

    problem: Given a linked list, remove the nth node from the end of list and return its head. For exam ...

  4. python(3)- 循环语句:从最内层跳出多层循环

    跳出多层循环:三层循环,最里层直接跳出3层 方法一: 在Python中,函数运行到return这一句就会停止,因此可以利用这一特性,将功能写成函数,终止多重循环 def work(): #定义函数 f ...

  5. Jenkins系列之-—08 实现SQL脚本批量执行

    公司内部推广DevOps,所有目前在维护阶段和开发阶段项目全部配置上了自动发布.采用Jenkins+SVN+ANT,之后批量执行SQL语句的实现提上日程 一.环境 Linux环境 安装ANT工具,且下 ...

  6. 【每日Scrum】第六天(4.16) TD学生助手Sprint1阶段性成果

    TD学生助手Sprint1阶段性成果(4.16) 任务看板 站立会议内容 组员 昨天 今天 困难 签到 刘铸辉 (组长) 和叶姐,静姐修改页面布局和图片显示,保证界面的亲切. 和大家一起做演示PPT, ...

  7. 【课程笔记】比特币和数字货币技术[Bitcoin and Cryptocurrency Technologies] week1

    源地址(可能要FQ):https://www.coursera.org/learn/cryptocurrency/home/welcome 1.1 Cryptographic Hash Functio ...

  8. StringUtils类使用 (转载)

    检查字符串是否为空或null或仅仅包含空格  String test = "";  String test1=" ";  String test2 = &quo ...

  9. java 多线程2(转载)

    http://www.cnblogs.com/DreamSea/archive/2012/01/11/JavaThread.html Ø线程的概述(Introduction) 线程是一个程序的多个执行 ...

  10. lua 定义类 就是这么简单

    在网上看到这样一段代码,真是误人子弟呀,具体就是: lua类的定义 代码如下: local clsNames = {} local __setmetatable = setmetatable loca ...