luogu传送门

最近学了一下动态dp,感觉没有想象的难。

动态DP

simple的DP是这样的:

给棵树,每个点给个权值,求一下最大权独立集。

动态DP是这样的:

给棵树,每个点给个权值还到处改,每次改的时候求一下最大权独立集。


题外话:

($NOIP2018$保卫王国)

大佬:动态$dp$板子直接秒。

神仙:这个是什么让我想一想……倍增?(然后当场切掉)

除了大佬除了神仙除了我:敲暴力。

我:(看错题了爆蛋)


几个前置知识:

(1)重链剖分

树剖时选择子树点数最多的儿子作重儿子。

重剖有几个性质:

1.终点一定是叶子。(树剖通性)

2.重链剖分序上一条重链是一个连续的区间。(树剖通性)

3.任意一个点走到根经过的轻链不超过$logn$条。(重剖特性

4.……

(2)矩阵

著名矩乘由于满足结合律可以用来加速递推/分治。

大佬们不知道咋想的就搞出了以下运算:

$$\begin{pmatrix}a00&a01\\a10&a11\end{pmatrix}+\begin{pmatrix}b00&b01\\b10&b11\end{pmatrix} $$

$$=\begin{pmatrix}max(a00+b00,a01+b10)&max(a00+b10,a01+b11)\\max(a10+b00,a11+b10)&max(a10+b01,a11+b11)\end{pmatrix}$$

这个东西竟然满足结合律……

我们可以用他去搞一些事情了。


树的最大权独立集:

设$f[u][0/1]$表示在点$u$不取/取的情况下点$u$子树内的最大权独立集的权值。

有$dp$如下:

$f[u][0]=\sum _{fa[v]=u} max(f[v][0],f[v][1])$

$f[u][1]=w[u] + \sum _{fa[v]=u} f[v][0]$

让轻重儿子分开讨论有:

$f[u][0]= max(f[son][0],f[son][1]) + \sum _{fa[v]=u,v!=son} max(f[v][0],f[v][1])$

$f[u][1]= f[son][0] +w[u]+\sum_{fa[v]=u,v!=son}f[v][0]$

设$g[u][0]=\sum _{fa[v]=u,v!=son} max(f[v][0],f[v][1]),g[u][1]=w[u]+\sum_{fa[v]=u,v!=son}f[v][0]$

写成上面矩阵的形式是这样的:

$f[u][0] = max(g[u][0]+f[son][0],g[u][0]+f[son][1])$

$f[u][1] = max(g[u][1]+f[son][0],-inf+f[son][1])$

就是$$\begin{pmatrix}f[son][0]&f[son][1]\end{pmatrix}+\begin{pmatrix}g[u][0]&g[u][1]\\g[u][0]&-inf\end{pmatrix}$$

$$=\begin{pmatrix}f[u][0]&f[u][1]\end{pmatrix}$$

神奇。

考虑到重链剖分序重链上的点是按深度从浅到深排列的,我们应该这样(不然线段树左右要反着合并比较恶心):

$$\begin{pmatrix}g[u][0]&g[u][0]\\g[u][1]&-inf\end{pmatrix}+\begin{pmatrix}f[son][0]\\f[son][1]\end{pmatrix}$$

$$=\begin{pmatrix}f[u][0]\\f[u][1]\end{pmatrix}$$

对于每个点保留带$g$的矩阵,这样一个点的$f$矩阵就是重链上从该点的矩阵一直向下加,加到叶子结点。

区间加法用线段树平衡树等数据结构维护。

每次修改之后,由于只会修改当前点到根路径上的$f$,我们可以爆跳树链。

修改点权,当前树链上只会修改当前点的$g[1]$。

树链之间转移时,考虑轻儿子的$f$对父节点$g$的影响。

我们可以求出修改前后链顶的$f$值,然后扔到原来方程里更新父亲的$g$。

重复上述操作一直更新到根。

由于重剖$logn$条轻链,时间复杂度$O(nlog^2n)$。

代码:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. typedef long long ll;
  6. const int N = 100050;
  7. const ll Inf = 0x3f3f3f3f3f3f3f3fll;
  8. template<typename T>
  9. inline void read(T&x)
  10. {
  11. T f = 1,c = 0;char ch=getchar();
  12. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  13. while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
  14. x = f*c;
  15. }
  16. ll F[N][2],G[N][2],k[N];
  17. int n,m,hed[N],cnt;
  18. struct EG
  19. {
  20. int to,nxt;
  21. }e[N<<1];
  22. void ae(int f,int t)
  23. {
  24. e[++cnt].to = t;
  25. e[cnt].nxt = hed[f];
  26. hed[f] = cnt;
  27. }
  28. int dep[N],siz[N],top[N],pot[N],fa[N],son[N],tin[N],pla[N],tim;
  29. struct mt
  30. {
  31. ll s[2][2];
  32. mt(){memset(s,0,sizeof(s));}
  33. mt(ll g0,ll g1){s[0][0]=s[0][1]=g0,s[1][0]=g1,s[1][1]=-Inf;}
  34. mt operator + (const mt&a)const
  35. {
  36. mt ret;
  37. ret.s[0][0]=max(s[0][0]+a.s[0][0],s[0][1]+a.s[1][0]);
  38. ret.s[0][1]=max(s[0][0]+a.s[0][1],s[0][1]+a.s[1][1]);
  39. ret.s[1][0]=max(s[1][0]+a.s[0][0],s[1][1]+a.s[1][0]);
  40. ret.s[1][1]=max(s[1][0]+a.s[0][1],s[1][1]+a.s[1][1]);
  41. return ret;
  42. }
  43. }t[N];
  44. struct segtree
  45. {
  46. mt w[N<<2];
  47. void update(int u){w[u]=w[u<<1]+w[u<<1|1];}
  48. void build(int l,int r,int u)
  49. {
  50. if(l==r){w[u]=t[pla[l]]=mt(G[pla[l]][0],G[pla[l]][1]);return ;}
  51. int mid = (l+r)>>1;
  52. build(l,mid,u<<1);
  53. build(mid+1,r,u<<1|1);
  54. update(u);
  55. }
  56. void insert(int l,int r,int u,int qx)
  57. {
  58. if(l==r){w[u]=t[pla[l]];return ;}
  59. int mid = (l+r)>>1;
  60. if(qx<=mid)insert(l,mid,u<<1,qx);
  61. else insert(mid+1,r,u<<1|1,qx);
  62. update(u);
  63. }
  64. mt query(int l,int r,int u,int ql,int qr)
  65. {
  66. if(l==ql&&r==qr)return w[u];
  67. int mid = (l+r)>>1;
  68. if(qr<=mid)return query(l,mid,u<<1,ql,qr);
  69. else if(ql>mid)return query(mid+1,r,u<<1|1,ql,qr);
  70. else return query(l,mid,u<<1,ql,mid)+query(mid+1,r,u<<1|1,mid+1,qr);
  71. }
  72. }tr;
  73. void dfs0(int u,int f)
  74. {
  75. fa[u] = f,siz[u] = 1,dep[u] = dep[f]+1;
  76. for(int j=hed[u];j;j=e[j].nxt)
  77. {
  78. int to = e[j].to;
  79. if(to==f)continue;
  80. dfs0(to,u);siz[u]+=siz[to];
  81. if(siz[to]>siz[son[u]])son[u]=to;
  82. }
  83. }
  84. void dfs1(int u,int Top)
  85. {
  86. top[u] = Top,pot[u] = u,tin[u] = ++tim,pla[tim] = u;
  87. if(son[u])dfs1(son[u],Top),pot[u]=pot[son[u]];
  88. for(int j=hed[u];j;j=e[j].nxt)
  89. {
  90. int to = e[j].to;
  91. if(to!=fa[u]&&to!=son[u])
  92. dfs1(to,to);
  93. }
  94. }
  95. void dp(int u)
  96. {
  97. F[u][0]=0,F[u][1]=k[u];
  98. for(int j=hed[u];j;j=e[j].nxt)
  99. {
  100. int to = e[j].to;
  101. if(to==fa[u])continue;
  102. dp(to);
  103. F[u][0]+=max(F[to][0],F[to][1]);
  104. F[u][1]+=F[to][0];
  105. }
  106. G[u][0] = F[u][0] - max(F[son[u]][0],F[son[u]][1]);
  107. G[u][1] = F[u][1] - F[son[u]][0];
  108. }
  109. mt get_mt(int x){return tr.query(1,n,1,tin[top[x]],tin[pot[x]]);}
  110. void chg(int x,int y)
  111. {
  112. t[x].s[1][0] += y-k[x],k[x] = y;
  113. while(x)
  114. {
  115. mt m0 = get_mt(x);
  116. tr.insert(1,n,1,tin[x]);
  117. mt m1 = get_mt(x);
  118. x = fa[top[x]];
  119. if(!x)break;
  120. t[x].s[0][0]+=max(m1.s[0][0],m1.s[1][0])-max(m0.s[0][0],m0.s[1][0]);
  121. t[x].s[0][1]=t[x].s[0][0];
  122. t[x].s[1][0]+=m1.s[0][0]-m0.s[0][0];
  123. }
  124. }
  125. int main()
  126. {
  127. // freopen("tt.in","r",stdin);
  128. read(n),read(m);
  129. for(int i=1;i<=n;i++)
  130. read(k[i]);
  131. for(int u,v,i=1;i<n;i++)
  132. read(u),read(v),ae(u,v),ae(v,u);
  133. dfs0(1,0),dfs1(1,1);
  134. dp(1);tr.build(1,n,1);
  135. for(int x,y,i=1;i<=m;i++)
  136. {
  137. read(x),read(y);
  138. chg(x,y);mt now = get_mt(1);
  139. printf("%lld\n",max(now.s[0][0],now.s[1][0]));
  140. }
  141. return 0;
  142. }

附保卫王国代码:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. typedef long long ll;
  6. const int N = 100050;
  7. const ll inf = 1e16;
  8. template<typename T>
  9. inline void read(T&x)
  10. {
  11. T f = 1,c = 0;char ch=getchar();
  12. while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  13. while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
  14. x = f*c;
  15. }
  16. int n,m,hed[N],cnt;
  17. ll p[N],F[N][2],G[N][2];
  18. char op[10];
  19. struct EG
  20. {
  21. int to,nxt;
  22. }e[N<<1];
  23. void ae(int f,int t)
  24. {
  25. e[++cnt].to = t;
  26. e[cnt].nxt = hed[f];
  27. hed[f] = cnt;
  28. }
  29. int dep[N],siz[N],fa[N],son[N],top[N],pot[N],tin[N],pla[N],tim;
  30. void dfs0(int u,int f)
  31. {
  32. fa[u] = f,siz[u] = 1,dep[u] = dep[f]+1;
  33. for(int j=hed[u];j;j=e[j].nxt)
  34. {
  35. int to = e[j].to;
  36. if(to==f)continue;
  37. dfs0(to,u);
  38. siz[u]+=siz[to];
  39. if(siz[to]>siz[son[u]])son[u]=to;
  40. }
  41. }
  42. void dfs1(int u,int Top)
  43. {
  44. top[u] = Top,tin[u] = ++tim,pla[tim] = u;
  45. if(son[u])dfs1(son[u],Top),pot[u]=pot[son[u]];
  46. else pot[u] = u;
  47. for(int j=hed[u];j;j=e[j].nxt)
  48. {
  49. int to = e[j].to;
  50. if(to!=fa[u]&&to!=son[u])
  51. dfs1(to,to);
  52. }
  53. }
  54. void dp(int u)
  55. {
  56. F[u][0] = 0,F[u][1] = p[u];
  57. for(int j=hed[u];j;j=e[j].nxt)
  58. {
  59. int to = e[j].to;
  60. if(to==fa[u])continue;
  61. dp(to);
  62. F[u][0] += F[to][1];
  63. F[u][1] += min(F[to][0],F[to][1]);
  64. }
  65. G[u][0] = F[u][0] - F[son[u]][1];
  66. G[u][1] = F[u][1] - min(F[son[u]][0],F[son[u]][1]);
  67. }
  68. struct mt
  69. {
  70. ll s[2][2];
  71. mt(){memset(s,0,sizeof(s));}
  72. mt(ll g0,ll g1){s[0][0]=inf,s[0][1]=g0,s[1][0]=s[1][1]=g1;}
  73. mt operator + (const mt&a)const
  74. {
  75. mt ret;
  76. ret.s[0][0]=min(s[0][0]+a.s[0][0],s[0][1]+a.s[1][0]);
  77. ret.s[0][1]=min(s[0][0]+a.s[0][1],s[0][1]+a.s[1][1]);
  78. ret.s[1][0]=min(s[1][0]+a.s[0][0],s[1][1]+a.s[1][0]);
  79. ret.s[1][1]=min(s[1][0]+a.s[0][1],s[1][1]+a.s[1][1]);
  80. return ret;
  81. }
  82. }t[N];
  83. struct segtree
  84. {
  85. mt w[N<<2];
  86. void update(int u){w[u]=w[u<<1]+w[u<<1|1];}
  87. void build(int l,int r,int u)
  88. {
  89. if(l==r){w[u]=t[pla[l]]=mt(G[pla[l]][0],G[pla[l]][1]);return ;}
  90. int mid = (l+r)>>1;
  91. build(l,mid,u<<1),build(mid+1,r,u<<1|1);
  92. update(u);
  93. }
  94. void insert(int l,int r,int u,int qx)
  95. {
  96. if(l==r){w[u]=t[pla[l]];return ;}
  97. int mid = (l+r)>>1;
  98. if(qx<=mid)insert(l,mid,u<<1,qx);
  99. else insert(mid+1,r,u<<1|1,qx);
  100. update(u);
  101. }
  102. mt query(int l,int r,int u,int ql,int qr)
  103. {
  104. if(l==ql&&r==qr)return w[u];
  105. int mid = (l+r)>>1;
  106. if(qr<=mid)return query(l,mid,u<<1,ql,qr);
  107. else if(ql>mid)return query(mid+1,r,u<<1|1,ql,qr);
  108. else return query(l,mid,u<<1,ql,mid)+query(mid+1,r,u<<1|1,mid+1,qr);
  109. }
  110. }tr;
  111. mt get_mt(int x){return tr.query(1,n,1,tin[top[x]],tin[pot[x]]);}
  112. void chg(int a,int x)
  113. {
  114. if(x==1)t[a].s[1][0]-=inf;
  115. else t[a].s[1][0]+=inf;
  116. t[a].s[1][1]=t[a].s[1][0];
  117. }
  118. void upd(int x)
  119. {
  120. while(x)
  121. {
  122. mt m0 = get_mt(x);
  123. tr.insert(1,n,1,tin[x]);
  124. mt m1 = get_mt(x);
  125. x = fa[top[x]];
  126. if(!x)break;
  127. t[x].s[0][1]+=m1.s[1][1]-m0.s[1][1];
  128. t[x].s[1][0]+=min(m1.s[0][1],m1.s[1][1])-min(m0.s[0][1],m0.s[1][1]);
  129. t[x].s[1][1]=t[x].s[1][0];
  130. }
  131. }
  132. int main()
  133. {
  134. // freopen("tt.in","r",stdin);
  135. read(n),read(m);scanf("%s",op);
  136. for(int i=1;i<=n;i++)read(p[i]);
  137. for(int u,v,i=1;i<n;i++)read(u),read(v),ae(u,v),ae(v,u);
  138. dfs0(1,0),dfs1(1,1);dp(1);tr.build(1,n,1);
  139. for(int a,x,b,y,i=1;i<=m;i++)
  140. {
  141. read(a),read(x),read(b),read(y);
  142. if((fa[a]==b||fa[b]==a)&&!x&&!y){puts("-1");continue;}
  143. chg(a,x);
  144. upd(a);
  145. chg(b,y);
  146. upd(b);
  147. mt M = get_mt(1);
  148. ll ans = min(M.s[0][1],M.s[1][1]);
  149. ans+=(x+y)*inf;
  150. printf("%lld\n",ans);
  151. chg(a,!x);upd(a);
  152. chg(b,!y);upd(b);
  153. }
  154. return 0;
  155. }

保卫王国

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

  1. [模板] 动态dp

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

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

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

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

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

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

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

  5. Luogu P4643 【模板】动态dp

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

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

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

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

    题解 在冬令营上听到冬眠的东西,现在都是板子了猫锟真的是好毒瘤啊(雾) (立个flag,我去thusc之前要把WC2018T1乱搞过去= =) 好的,我们可以参考猫锟的动态动态dp的课件,然后你发现你 ...

  8. 「LGP4719【模板】动态dp」

    题目 尽管知道这个东西应该不会考了,但是还是学一学吧 哎要是去年noip之前学该多好 动态\(dp\)就是允许修改的一个\(dp\),比如这道题,我们都知道这是一个树上最大点权独立集 众所周知方程长这 ...

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

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

  10. P4719 【模板】动态dp

    \(\color{#0066ff}{ 题目描述 }\) 给定一棵\(n\)个点的树,点带点权. 有\(m\)次操作,每次操作给定\(x,y\),表示修改点xx的权值为\(y\). 你需要在每次操作之后 ...

随机推荐

  1. Haar小波分析

    一 尺度函数与小波函数 基本尺度函数定义为:,对其向右平移任意 k 个单位,构成函数族 , 该函数族在 空间中正交,证明如下: 1 : 2 当 m 不等于 k 时, 函数族  构成一组正交基,并形成  ...

  2. 走进Task(2):Task 的回调执行与 await

    目录 前言 Task.ContinueWith ContinueWith 的产物:ContinuationTask 额外的参数 回调的容器:TaskContinuation Task.Continue ...

  3. Sunlogin RCE漏洞分析和使用

    介绍   前两天网上曝出了关于向日葵远控工具(Sunlogin)Windows个人版的RCE漏洞POC.因为利用简单并且网上出现了公开的自动化扫描脚本,所以测试的人很多,也出现了一些真实攻击.漏洞的问 ...

  4. 第二章 初始MySQL 语法

    1.使用MySQL的优势 运行速度快.MySQL体积小,命令执行的速度快: 使用成本低.MySQL是开源的. 容易使用. 可移植性强.MySQL能够运行于多种系统平台之上,windows,linux, ...

  5. java实现 TCP通信

    //服务端import com.hl.bluetooth.util.CRC16; import com.hl.bluetooth.util.FrameCheckFailedException; imp ...

  6. ShaderLab实现Vignette过场动画效果

    实现Vignette过场动画效果 postprocessing中有渐晕效果(Vignette),镜头可以由边缘往中间慢慢变黑: 但是我打包WebGL的时候提示我postprocessing,GPU不支 ...

  7. ctf平台

    CTF靶场 蓝鲸安全:http://whalectf.xin bugku:https://ctf.bugku.com XCTF攻防世界:https://adworld.xctf.org.cn/ i春秋 ...

  8. apt-clone安装与使用

    当我们在基于 Ubuntu/Debian 的系统上使用apt-clone ,包安装会变得更加容易. 如果你需要在少量系统上安装相同的软件包时,apt-clone 会适合你. 什么是 apt-clone ...

  9. Mono创始人 Miguel de Icaza今天离开微软

    2016年,微软突然宣布收购移动工具开发商Xamarin,后者是位于美国加利福尼亚,据称微软收购Xamarin交易价格在4亿到5亿美元之间.因此,微软获得了著名的开源倡导者和开发人员Miguel de ...

  10. Mac下的平铺式桌面 - Yabai

    Mac下的平铺式桌面 - Yabai 近来无事,凑着周末休息的时间,想折腾一下 Mac.很久之前就有朋友给我推荐过一款名为"Yabai"的平铺式桌面管理软件,今天,就折腾起来了. ...