题意

我理解的动态DP:

发现DP可以写成矩阵的形式,因此用数据结构维护矩阵乘积。

对于这道题,显然有DP:

\(f_{x,0/1}\)表示\(x\)的子树中,x选/不选的最大点独立集。

\(f_{x,0}=\sum\limits_{y\in son_x}\max(f_{y,0},f_{y,1}),f_{x,1}=\sum\limits_{y\in sno_x}f_{y,0}+a_x\)

既然在树上,就用树剖或者LCT解决,本质都是将树拆成链,这里用树剖。

设\(son_x\)表示\(x\)的重儿子,\(g_{x,0/1}\)表示除去\(son_x\)后的\(f_{x,0}\)的值。

有:

\(f_{x,0}=g_{x,0}+\max(f_{son_x,0},f_{son_x,1}),f_{x,1}=g_{x,1}+f_{son_x,0}\),注意\(g_{x,1}\)初值为\(a_x\)。

DP写成矩阵的形式:

\(\begin{bmatrix}g_{x,0}&g_{x,0}\\g_{x,1}& 0\end{bmatrix}\begin{bmatrix}f_{y,0}\\ f_{y,1}\end{bmatrix}=\begin{bmatrix}f_{x,0}\\ f_{x,1}\end{bmatrix}\)

注意这里的矩乘长这样:

Mat operator*(Mat a,Mat b)
{
Mat res;
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
res[i][j]=max(res[i][j],a[i][k]+b[k][j]);
return res;
}

之后就正常树剖修改查询即可

code:

#include<bits/stdc++.h>
using namespace std;
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
const int maxn=1e5+10;
int n,m,cnt,tim;
int head[maxn],a[maxn],size[maxn],pre[maxn],dep[maxn],son[maxn],dfn[maxn],pos[maxn],top[maxn],ed[maxn];
int f[maxn][2];
struct edge{int to,nxt;}e[maxn<<1];
struct Mat
{
int a[5][5];
Mat(){memset(a,-0x3f,sizeof(a));}
int* operator[](int i){return a[i];}
}val[maxn],seg[maxn<<2];
Mat operator*(Mat a,Mat b)
{
Mat res;
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)
for(int k=1;k<=2;k++)
res[i][j]=max(res[i][j],a[i][k]+b[k][j]);
return res;
}
inline void add(int u,int v)
{
e[++cnt].nxt=head[u];
head[u]=cnt;
e[cnt].to=v;
}
void dfs1(int x,int fa)
{
dep[x]=dep[fa]+1;pre[x]=fa;size[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa)continue;
dfs1(y,x);size[x]+=size[y];
if(size[son[x]]<size[y])son[x]=y;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;pos[tim]=x;top[x]=tp;ed[tp]=max(ed[tp],tim);
f[x][0]=0,f[x][1]=a[x];
val[x][1][1]=val[x][1][2]=0;
val[x][2][1]=a[x];
if(son[x])
{
dfs2(son[x],tp);
f[x][0]+=max(f[son[x]][0],f[son[x]][1]);
f[x][1]+=f[son[x]][0];
}
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==pre[x]||y==son[x])continue;
dfs2(y,y);
f[x][0]+=max(f[y][0],f[y][1]);
f[x][1]+=f[y][0];
val[x][1][1]+=max(f[y][0],f[y][1]);
val[x][1][2]=val[x][1][1];
val[x][2][1]+=f[y][0];
}
}
inline void up(int p){seg[p]=seg[ls(p)]*seg[rs(p)];}
void build(int p,int l,int r)
{
if(l==r){seg[p]=val[pos[l]];return;}
int mid=(l+r)>>1;
build(ls(p),l,mid);build(rs(p),mid+1,r);
up(p);
}
void change(int p,int l,int r,int k)
{
if(l==r){seg[p]=val[pos[k]];return;}
int mid=(l+r)>>1;
if(k<=mid)change(ls(p),l,mid,k);
else change(rs(p),mid+1,r,k);
up(p);
}
Mat query(int p,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)return seg[p];
int mid=(l+r)>>1;
if(qr<=mid)return query(ls(p),l,mid,ql,qr);
else if(ql>mid)return query(rs(p),mid+1,r,ql,qr);
else return query(ls(p),l,mid,ql,qr)*query(rs(p),mid+1,r,ql,qr);
}
inline void trchange(int x,int k)
{
val[x][2][1]+=k-a[x];
a[x]=k;
Mat tmp1,tmp2;
while(x)
{
tmp1=query(1,1,n,dfn[top[x]],ed[top[x]]);
change(1,1,n,dfn[x]);
tmp2=query(1,1,n,dfn[top[x]],ed[top[x]]);
x=pre[top[x]];
val[x][1][1]+=max(tmp2[1][1],tmp2[2][1])-max(tmp1[1][1],tmp1[2][1]);
val[x][1][2]=val[x][1][1];
val[x][2][1]+=tmp2[1][1]-tmp1[1][1];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs1(1,0);dfs2(1,1);
build(1,1,n);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
trchange(x,y);
a[x]=y;
Mat res=query(1,1,n,dfn[1],ed[1]);
printf("%d\n",max(res[1][1],res[2][1]));
}
return 0;
}

luoguP4719 【模板】动态 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. luoguP4719 【模板】动态 DP 线段树+树链剖分+矩阵乘法+动态DP

    题目描述 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y,表示修改点x的权值为y. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入输出格式 输入格式: 第一行,n,m分 ...

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

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

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

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

  6. Luogu P4643 【模板】动态dp

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

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

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

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

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

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

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

随机推荐

  1. jenkins下载插件无插件显示+离线下载插件方法

    1.打开Jenkins插件管理,可选插件为空,无法选择自己需要的插件进行下载 打开插件管理的“高级”选项,在升级站点填写 http://mirror.xmission.com/jenkins/upda ...

  2. mysql索引类型:FULLTEXT、NORMAL、SPATIAL、UNIQUE的详细介绍(转)

    Normal 普通索引 表示普通索引,大多数情况下都可以使用 Unique 唯一索引 表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号用作索引时,可设置为unique 约束唯一标识 ...

  3. P4762 [CERC2014]Virus synthesis

    题意 真是道回文自动机好题. 首先考虑答案必定是一个回文串+剩余部分的形式,因此可以建出回文自动机,之后考虑每个长度为偶数的回文串. 对于一个长度为偶数的回文串,设它在回文自动机上对应的节点为\(x\ ...

  4. C# 二维数组 转换成 DataTable

    C# 数据转换 Overview C# 窗体操作中,有些比较特别的操作.但是为了方便我们不得不使用一些比较特别的手段. C#中二维数组转DataTable 首先,我们看一下我对二维数组的数据处理.这次 ...

  5. IT兄弟连 HTML5教程 HTML文件的主体结构

    每个页面都是一个独立的HTML文档,每个HTML文档的主体结构又都是相同的,而且在一个文档中这样的主体结构只能声明一次.可以简单的将HTML文档主体结构分为两部分,一部分是定义文档类型,HTML5中声 ...

  6. C#实现数据回滚,A事件和B事件同时执行,其中任何一个事件执行失败,都会返回失败

    /// <summary> /// 执行数据库回滚操作,用于sql语句执行失败后,恢复执行前的数据 /// </summary> /// <param name=&quo ...

  7. C++入门到理解阶段二基础篇(4)——C++运算符

    目录 算术运算符(进行四则运算) 赋值运算符(表达式的值赋给变量) 比较运算符(表达是比较,返回一个真值或假值) 逻辑运算符(返回表格式的结果真或假) 位运算符 杂项运算符 C++ 中的运算符优先级 ...

  8. git查看/修改个人信息-用户名邮箱

    我们在使用git作为仓库管理工具时,要设置自己Git的用户名和邮箱,要不然大家一块开发时不知道谁是谁,不知道谁提交的. 另外,当我们用自己的电脑开发时你可能设置的是一个你喜欢的昵称,所以那就得改一下. ...

  9. 资源推荐:PPT快闪资源合集附配套字体下载

    样例ppt下载 搜索公众号“拒收”或扫码关注公众号 回复关键字“快闪ppt”获取全部福利 本公众号只出精品,拒收劣质 或者点击菜单链接获取获取全部资源

  10. eclipse彻底去除validation(彻底解决编辑js文件的卡顿问题)

    Eclipse中默认的JS编辑器非常慢,尤其在拷贝粘贴代码时,CPU总是占用很高甚至到100%,也就导致了编辑起来很卡. 这是因为Eclipse中带的Validate功能导致的,这个鸡肋的功能简直让人 ...