【模板】"动态 DP"&动态树分治
CLII.【模板】"动态 DP"&动态树分治
裸的树上最大独立集怎么做?设 表示在 子树中, 不选的最大答案; 表示在 子树中, 选的最大答案。则有
现在要支持修改,怎么办?
发现,每次修改,DP值受到影响的位置是从修改位置到根的一条路径。听上去像是树剖应用的场景?
于是我们考虑树剖,并设 表示忽略 的重儿子时的答案。则,,其中 是 的重儿子。
现在,我们考虑将其转成矩阵的形式。则有
其中,矩阵乘法的定义是 。手推可得其满足结合律。
于是,我们考虑从一个叶子的 数组开始(令人惊喜的是,叶子节点的 与 是相等的(因为其不存在儿子),这使得转移很方便),一路往上乘矩阵,即可得到该叶子所在重链上任意节点的 数组。并且,因为任意重链的结尾节点都是叶子,所以通过上述方法可以求出任意节点的 数组。
现在考虑带修。发现,一次修改只会影响到根的路径上所有重链顶的父亲的 数组,而这样的父亲最多只有 个,故直接暴力跳链修改父亲即可。
时间复杂度 。
代码:
#include<bits/stdc++.h>
using namespace std;
const int inf=0xd0d0d0d0;
int n,m,a[100100],f[100100][2],g[100100][2];//0:self unchosen 1:self chosen
int fa[100100],son[100100],sz[100100],dfn[100100],rev[100100],top[100100],bot[100100],tot;
vector<int>v[100100];
void dfs1(int x){
sz[x]=1,f[x][0]=0,f[x][1]=a[x];
for(auto y:v[x])if(y!=fa[x]){
fa[y]=x,dfs1(y),sz[x]+=sz[y];
if(sz[son[x]]<sz[y])son[x]=y;
f[x][0]+=max(f[y][0],f[y][1]),f[x][1]+=f[y][0];
}
}
void dfs2(int x){
dfn[x]=++tot,rev[tot]=x;if(!top[x])top[x]=x;if(son[x])top[son[x]]=top[x],dfs2(son[x]);else bot[top[x]]=x;
for(auto y:v[x])if(y!=fa[x]&&y!=son[x])dfs2(y),g[x][0]+=max(f[y][0],f[y][1]),g[x][1]+=f[y][0];
}
#define lson x<<1
#define rson x<<1|1
#define mid ((l+r)>>1)
struct Matrix{
int t[2][2];
Matrix(){memset(t,inf,sizeof(t));}
Matrix(int x){t[1][0]=t[0][1]=inf,t[0][0]=t[1][1]=0;}
int*operator[](const int&x){return t[x];}
friend Matrix operator*(Matrix u,Matrix v){Matrix w;for(int i=0;i<2;i++)for(int j=0;j<2;j++)for(int k=0;k<2;k++)w[i][j]=max(w[i][j],u[i][k]+v[k][j]);return w;}
void print()const{for(int i=0;i<2;i++,puts(""))for(int j=0;j<2;j++)printf("%d ",t[i][j]);}
int zero(){return max(max(t[0][0],t[1][0]),max(t[0][1],t[1][1]));}
int one(){return max(t[0][0],t[1][0]);}
}seg[400100];
Matrix Generate(int x){Matrix M;M[0][0]=g[x][0],M[1][0]=g[x][0],M[0][1]=g[x][1],M[1][1]=inf;return M;}
void build(int x,int l,int r){if(l==r)seg[x]=Generate(rev[l]);else build(lson,l,mid),build(rson,mid+1,r),seg[x]=seg[rson]*seg[lson];}
Matrix query(int x,int l,int r,int L,int R){if(l>R||r<L)return Matrix(0);if(L<=l&&r<=R)return seg[x];return query(rson,mid+1,r,L,R)*query(lson,l,mid,L,R);}
void reset(int x,int l,int r,int P){if(l>P||r<P)return;if(l==r)seg[x]=Generate(rev[P]);else reset(lson,l,mid,P),reset(rson,mid+1,r,P),seg[x]=seg[rson]*seg[lson];}
int modify(int x,int y){
Matrix M;
for(int i=x;top[i]!=1;)M=query(1,1,n,dfn[top[i]],dfn[bot[top[i]]]),i=fa[top[i]],g[i][0]-=M.zero(),g[i][1]-=M.one();
g[x][1]+=y-a[x],a[x]=y,reset(1,1,n,dfn[x]);
for(int i=x;top[i]!=1;)M=query(1,1,n,dfn[top[i]],dfn[bot[top[i]]]),i=fa[top[i]],g[i][0]+=M.zero(),g[i][1]+=M.one(),reset(1,1,n,dfn[i]);
M=query(1,1,n,1,dfn[bot[1]]);
return M.zero();
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),g[i][1]=a[i];
for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
dfs1(1),dfs2(1),build(1,1,n);
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),printf("%d\n",modify(x,y));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?