洛谷P4719 动态DP —— 动态DP(树剖+矩乘)
题目: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 提取出一条重链。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1)
#define ls (x<<1)
#define rs (x<<1|1)
using namespace std;
int const xn=1e5+,inf=1e9;
int n,m,a[xn],hd[xn],ct,to[xn<<],nxt[xn<<],dfn[xn],tim,id[xn],top[xn];
int fa[xn],siz[xn],son[xn],f[xn][],ed[xn];
struct N{
int a[][];
N(){a[][]=a[][]=; a[][]=a[][]=-inf;}
N operator * (const N &y) const
{
N ret;
for(int i=;i<;i++)
for(int k=;k<;k++)
for(int j=;j<;j++)
ret.a[i][j]=max(ret.a[i][j],a[i][k]+y.a[k][j]);
return ret;
}
}s[xn],t[xn<<];
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=(ret<<)+(ret<<)+ch-'',ch=getchar();
return f?ret:-ret;
}
void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
int maxx(int a,int b){return a>b?a:b;}
void dfs(int x,int ff)
{
fa[x]=ff; siz[x]=; f[x][]=a[x];
for(int i=hd[x],u;i;i=nxt[i])
{
if((u=to[i])==ff)continue;
dfs(u,x); siz[x]+=siz[u];
if(siz[u]>siz[son[x]])son[x]=u;
f[x][]+=f[u][]; f[x][]+=maxx(f[u][],f[u][]);
}
}
void dfsx(int x)
{
dfn[x]=++tim; id[tim]=x;
if(son[x])top[son[x]]=top[x],dfsx(son[x]);
for(int i=hd[x],u;i;i=nxt[i])
if((u=to[i])!=fa[x]&&u!=son[x])top[u]=u,dfsx(u);
if(!son[x])ed[top[x]]=dfn[x];//!!链底
//ed[x]=tim;
}
void build(int x,int l,int r)
{
if(l==r)
{
int nw=id[l],g0=,g1=a[nw];
for(int i=hd[nw],u;i;i=nxt[i])
if((u=to[i])!=fa[nw]&&u!=son[nw])g0+=maxx(f[u][],f[u][]),g1+=f[u][];
t[x].a[][]=t[x].a[][]=g0; t[x].a[][]=g1;
s[l]=t[x]; return;//s[l]!
}
build(ls,l,mid); build(rs,mid+,r);
t[x]=t[ls]*t[rs];
}
N query(int x,int l,int r,int L,int R)
{
if(l>=L&&r<=R)return t[x];
if(mid>=R)return query(ls,l,mid,L,R);
if(mid<L)return query(rs,mid+,r,L,R);
return query(ls,l,mid,L,R)*query(rs,mid+,r,L,R);
}
N get(int x){return query(,,n,dfn[x],ed[x]);}
void chg(int x,int l,int r,int pos)
{
if(l==r){t[x]=s[l]; return;}//!s[x]!
if(pos<=mid)chg(ls,l,mid,pos);
else chg(rs,mid+,r,pos);
t[x]=t[ls]*t[rs];
}
void work(int x,int ss)
{
s[dfn[x]].a[][]+=ss-a[x]; a[x]=ss;//dfn[x]
N nw,pr;
while()
{
pr=get(top[x]); chg(,,n,dfn[x]); nw=get(top[x]);
x=fa[top[x]]; if(!x)return;
s[dfn[x]].a[][]+=maxx(nw.a[][],nw.a[][])-maxx(pr.a[][],pr.a[][]);
s[dfn[x]].a[][]=s[dfn[x]].a[][];
s[dfn[x]].a[][]+=nw.a[][]-pr.a[][];//dfn[x]
}
}
int main()
{
n=rd(); m=rd();
for(int i=;i<=n;i++)a[i]=rd();
for(int i=,x,y;i<n;i++)x=rd(),y=rd(),add(x,y),add(y,x);
dfs(,); top[]=; dfsx(); build(,,n);
for(int i=,x,y;i<=m;i++)
{
x=rd(); y=rd(); work(x,y); N tmp=get();
printf("%d\n",maxx(tmp.a[][],tmp.a[][]));
}
return ;
}
洛谷P4719 动态DP —— 动态DP(树剖+矩乘)的更多相关文章
- 洛谷P1505 [国家集训队]旅游(树剖+线段树)
传送门 这该死的码农题…… 把每一条边变为它连接的两个点中深度较浅的那一个,然后就是一堆单点修改/路径查询,不讲了 这里就讲一下怎么搞路径取反,只要打一个标记就好了,然后把区间和取反,最大最小值交换然 ...
- 洛谷P4719 动态dp
动态DP其实挺简单一个东西. 把DP值的定义改成去掉重儿子之后的DP值. 重链上的答案就用线段树/lct维护,维护子段/矩阵都可以.其实本质上差不多... 修改的时候在log个线段树上修改.轻儿子所在 ...
- 洛谷 P5279 - [ZJOI2019]麻将(dp 套 dp)
洛谷题面传送门 一道 dp 套 dp 的 immortal tea 首先考虑如何判断一套牌是否已经胡牌了,考虑 \(dp\).我们考虑将所有牌按权值大小从大到小排成一列,那我们设 \(dp_ ...
- 洛谷 P3373 【模板】线段树 2
洛谷 P3373 [模板]线段树 2 洛谷传送门 题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xx 将某区间每一个数加上 xx 求出某区间每一个数的和 输入格式 第一 ...
- 洛谷P4719 【模板】"动态 DP"&动态树分治
[模板]"动态 DP"&动态树分治 第一道动态\(DP\)的题,只会用树剖来做,全局平衡二叉树什么的就以后再学吧 所谓动态\(DP\),就是在原本的\(DP\)求解的问题上 ...
- 【洛谷P4719】动态dp 动态dp模板
题目大意:给你一颗$n$个点的树,点有点权,有$m$次操作,每次操作给定$x$,$y$,表示修改点$x$的权值为$y$. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 数据范围:$n,m≤ ...
- Bzoj3197/洛谷3296 [SDOI2013]刺客信条assassin(树的重心+树Hash+树形DP+KM)
题面 Bzoj 洛谷 题解 (除了代码均摘自喻队的博客,可是他退役了) 首先固定一棵树,枚举另一棵树,显然另一棵树只有与这棵树同构才有可能产生贡献 如果固定的树以重心为根,那么另一棵树最多就只有重心为 ...
- 洛谷 P2495 [SDOI2011]消耗战(虚树,dp)
题面 洛谷 题解 虚树+dp 关于虚树 了解一下 具体实现 inline void insert(int x) { if (top == 1) {s[++top] = x; return ;} int ...
- bzoj3295 洛谷P3157、1393 动态逆序对——树套树
题目:bzoj3295 https://www.lydsy.com/JudgeOnline/problem.php?id=3295 洛谷 P3157(同一道题) https://www.luogu.o ...
随机推荐
- 转: svn服务器路径名修改(不需要全部重新拉取文件)
svn路径名修改之后, 一大波的研发代码都可能面临变更.还有有一个svn relote神器 大家可以借助各自的SVN工具中哦relote命令完成路径的切换,而不需要全部重新download所有的新路径 ...
- Django进阶之Form
Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 一.创建Form类 #!/usr/bin/en ...
- Google Chrome浏览器之删除Goolge搜索结果重定向插件Remove Google Redirects
https://chrome.google.com/webstore/detail/remove-google-redirects/ccenmflbeofaceccfhhggbagkblihpoh?h ...
- Python 模块之 ConfigParser: 用 Python 解析配置文件
在程序中使用配置文件来灵活的配置一些参数是一件很常见的事情,配置文件的解析并不复杂,在 Python 里更是如此,在官方发布的库中就包含有做这件事情的库,那就是 ConfigParser,这里简单的做 ...
- PCB常用单位转换 mil 英尺
PCB常用单位转换 mil 英尺 相关常用单位 1mil = 0.0254mm 100mil = 2.54mm 1英寸 = 1000mil = 2.54cm 1英尺 = 12英寸 ...
- 李洪强iOS开发之带placeHolder的Textview
李洪强iOS开发之带placeHolder的Textview 01 - 创建工过程,定义全局属性,遵守textview的代理协议 02 - 添加一个textview和一个label 03 - 实现 ...
- Python中十六进制和字符串的转换(转载)
调用Python内置int()函数把该字串转为数字.以下为在Python解释器编程环境下的操作示范: 把十六进制的字串转为十进制数字:Python代码>>> print int('f ...
- Mvc Autofac构造器注入
新建MVC项目,添加程序集引用 定义接口ILog public interface ILog { string Save(string message); } 类TxtLog实现接口ILog publ ...
- 学习某些API的方法
学习某些 API 的方法 这里的 API 可能是某个系统平台,开发包,开发平台,开发工具等等,因为任何系统和技术方法提供给开发者的打包方式都是一系列 API . 无论你有在哪一层级开发,从硬件驱动到系 ...
- EasyDarwin开源云平台接入海康威视EasyCamera摄像机之快照获取与上传
本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376 EasyCamera开源摄像机拥有获取摄像机实时快照并上传至EasyDarwin云平 ...