我们经常会遇到一些问题,是一些dp的模型,但是加上了什么待修改强制在线之类的,十分毒瘤,如果能有一个模式化的东西解决这类问题就会非常好。

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

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

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

如果不带修改,那就是一个最简单是树形dp问题。

我们设一个dp[i][0],dp[i][1]表示以i为根的子树

动态dp能够使用的一个前提就是它的转移是线性的,这样我们就可以用矩阵乘法实现快速转移了。

注意:这里的矩阵乘法是广义的,中间运算不一定是乘法,最后也不一定是求和,只要能满足矩阵乘法的性质就可以了。

重链剖分

这也是动态dp比较关键的内容,因为问题在树上,树的每个节点都可能有多个儿子节点,直接算贡献比较麻烦。

所以用重链剖分只保留一个儿子,其他的儿子放在一起统一计算,这样我们就把一个树上问题转化成了序列上的问题。

比如这道题,我们把树轻重链划分完后。

我们把轻子树的答案算完后直接加入状态中,然后答案就变成了一条重链的矩阵连乘积,用线段树维护矩阵的乘积即可。

每次修改时,根据重链剖分,答案包含这个点的位置最多有log个,所以每次就对这些位置修改就好了 。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100002
using namespace std;
typedef long long ll;
int tot,head[N],size[N],deep[N],fa[N],son[N],top[N],dp[N][],dfn[N],tag[N],ed[N],a[N],cntt,ls[N<<],rs[N<<],n,m,root;
inline ll rd(){
ll x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct edge{int n,to;}e[N<<];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
struct matrix{
int a[][];
matrix(){memset(a,-0x3f,sizeof(a));}
matrix operator *(const matrix &b)const{
matrix c;
for(int i=;i<;++i)
for(int j=;j<;++j)
for(int k=;k<;++k)
c.a[i][j]=max(c.a[i][j],a[i][k]+b.a[k][j]);
return c;
}
}data[N],tr[N<<];
void dfs1(int u){
size[u]=;
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa[u]){
int v=e[i].to;deep[v]=deep[u]+;fa[v]=u;
dfs1(v);
size[u]+=size[v];
if(size[v]>size[son[u]])son[u]=v;
}
}
void dfs2(int u){
dfn[u]=++dfn[];tag[dfn[]]=u;
if(!top[u])top[u]=u;
ed[top[u]]=max(ed[top[u]],dfn[u]);
data[u].a[][]=data[u].a[][]=;
data[u].a[][]=a[u];
dp[u][]=a[u];
if(son[u]){
top[son[u]]=top[u],dfs2(son[u]);
dp[u][]+=max(dp[son[u]][],dp[son[u]][]);
dp[u][]+=dp[son[u]][];
}
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa[u]&&e[i].to!=son[u]){
int v=e[i].to;dfs2(v);
dp[u][]+=max(dp[v][],dp[v][]);
dp[u][]+=dp[v][];
data[u].a[][]+=max(dp[v][],dp[v][]);
data[u].a[][]+=max(dp[v][],dp[v][]);
data[u].a[][]+=dp[v][];
}
}
void build(int &cnt,int l,int r){
if(!cnt)cnt=++cntt;
if(l==r){tr[cnt]=data[tag[l]];return;}
int mid=(l+r)>>;
build(ls[cnt],l,mid);build(rs[cnt],mid+,r);
tr[cnt]=tr[ls[cnt]]*tr[rs[cnt]];
}
void upd(int cnt,int l,int r,int x){
if(l==r){tr[cnt]=data[tag[x]];return;}
int mid=(l+r)>>;
if(mid>=x)upd(ls[cnt],l,mid,x);
else upd(rs[cnt],mid+,r,x);
tr[cnt]=tr[ls[cnt]]*tr[rs[cnt]];
}
matrix query(int cnt,int l,int r,int L,int R){
if(l>=L&&r<=R)return tr[cnt];
int mid=(l+r)>>;
if(mid>=L&&mid<R)return query(ls[cnt],l,mid,L,R)*query(rs[cnt],mid+,r,L,R);
else if(mid>=L)return query(ls[cnt],l,mid,L,R);
else return query(rs[cnt],mid+,r,L,R);
}
void _upd(int u,int vall){
data[u].a[][]+=vall-a[u];
a[u]=vall;
matrix now,pre;
while(u){
pre=query(,,n,dfn[top[u]],ed[top[u]]);
upd(,,n,dfn[u]);
now=query(,,n,dfn[top[u]],ed[top[u]]);
u=fa[top[u]];
data[u].a[][]+=max(now.a[][],now.a[][])-max(pre.a[][],pre.a[][]);
data[u].a[][]=data[u].a[][];
data[u].a[][]+=now.a[][]-pre.a[][];
}
}
int main(){
n=rd();m=rd();
for(int i=;i<=n;++i)a[i]=rd();
int u,v;
for(int i=;i<n;++i){
u=rd();v=rd();
add(u,v);add(v,u);
}
dfs1();dfs2();
build(root,,n);
while(m--){
u=rd();v=rd();
_upd(u,v);
matrix nowans=query(,,n,dfn[],ed[]);
printf("%d\n",max(nowans.a[][],max(nowans.a[][],nowans.a[][])));
}
return ;
}

动态dp学习笔记的更多相关文章

  1. 动态 DP 学习笔记

    不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...

  2. [总结] 动态DP学习笔记

    学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...

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

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

  4. 数位DP学习笔记

    数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...

  5. DP学习笔记

    DP学习笔记 可是记下来有什么用呢?我又不会 笨蛋你以后就会了 完全背包问题 先理解初始的DP方程: void solve() { for(int i=0;i<;i++) for(int j=0 ...

  6. 树形DP 学习笔记

    树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...

  7. 斜率优化DP学习笔记

    先摆上学习的文章: orzzz:斜率优化dp学习 Accept:斜率优化DP 感谢dalao们的讲解,还是十分清晰的 斜率优化$DP$的本质是,通过转移的一些性质,避免枚举地得到最优转移 经典题:HD ...

  8. 插头DP学习笔记——从入门到……????

    我们今天来学习插头DP??? BZOJ 2595:[Wc2008]游览计划 Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该 ...

  9. 树形$dp$学习笔记

    今天学习了树形\(dp\),一开始浏览各大\(blog\),发现都\(TM\)是题,连个入门的\(blog\)都没有,体验极差.所以我立志要写一篇可以让初学树形\(dp\)的童鞋快速入门. 树形\(d ...

随机推荐

  1. LEDAPS1.3.0版本移植到windows平台----HuPm参数初始化模块

    这个是2012年左右放在百度空间的,谁知百度空间关闭...转移到博客园. 最近项目用到3.1.2版本的LEDAPS,新版本的使用情况会在后续文章中慢慢丰富. LEDAPS的调用顺序是:HuPm--&g ...

  2. C++ 11 Lambda表达式

    C++11的一大亮点就是引入了Lambda表达式.利用Lambda表达式,可以方便的定义和创建匿名函数.对于C++这门语言来说来说,“Lambda表达式”或“匿名函数”这些概念听起来好像很深奥,但很多 ...

  3. Windows应急响应常识

    Windows 应急响应 常见事件ID 1102 清理审计日志 4624 账号登陆成功 4625 账号登陆失败 4672 授予特殊权限 4720 创建用户 4726 删除用户 4728 将成员添加到启 ...

  4. Java开发学习心得(一):SSM环境搭建

    目录 Java开发学习心得(一):SSM环境搭建 1 SSM框架 1.1 Spring Framework 1.2 Spring MVC Java开发学习心得(一):SSM环境搭建 有一点.NET的开 ...

  5. 多线程控制工具类--倒计时器CountDownLatch的使用(模仿火箭发射)

    package com.thread.test.Lock; import java.util.Random; import java.util.concurrent.CountDownLatch; i ...

  6. 多线程--wait()和notify(),Thread中的等待和唤醒方法

    package com.thread.test; public class SimpleMN { final static Object object = new Object(); public s ...

  7. 数据库訪问技术之JDBC

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zhuojiajin/article/details/32150883     在了解JDBC之前呢, ...

  8. macos 下usb键盘问题.

    Mac 与PC键盘的对比及快捷键(黑苹果) https://www.jianshu.com/p/240f31f6f81a 剩下的就是 系统偏好设置 - 键盘 - 修饰键 - USB键盘(目标键盘) 把 ...

  9. day14- 面向对象基础(一)

    今天开始写关于面向对象的内容,面向对象是编程思想中一大思想,由于日常使用中经常会用到,本次主要是对于我个人认为重点的基础知识进行整理,不会特别详细的书写. 1.面向过程与面向对象的区别 2.类 3.类 ...

  10. 前端——DOM

    什么是DOM? DOM是W3C(万维网联盟)的标准,是Document Object Model(文档对象模型)的缩写,它定义了访问HTML和XML文档的标准: “W3C文档对象模型(DOM)是中立于 ...