D-DP
必备知识
树链剖分,最大权独立集(即没有上司的舞会(树上DP)),矩阵乘法;
D-DP
模版简述
模板
关于动态DP,其实是关于一类动态修改点权的问题,但是很难去处理;
我们平常的DP经常是离线DP,而当在线时,就会出现事故;
D-DP是关于求最大权独立集的,支持动态修改点值,其思想即DP的通项递推式改为矩阵乘的形式进行递推,而树上问题就可以使用树链剖分提前处理出区间矩阵乘;
大家肯定都会 \(O(nm)\) 做法,即暴力修改点值进行DP.
求最大权独立集的DP式子为 \(f[x][1]=\sum f[y][0] , f[x][0]=\sum max(f[y][1],f[y][0])\) ,如果我们将其改成矩阵乘的形式,就变成了下面这样;
\]
这个式子并不完整,这只是从一个点的矩阵乘,这个式子只是类似于矩阵乘,我们可以重新定义一下矩阵乘的定义,即将加法改为 \(max\) ,将乘法改为加法,那么就可以通项递推了;
但是这个式子只限于从一个点递推过来,那么其他点怎么办?
没有办法...但是这是一个思路,这个式子用线段树维护,可以让我们可以快速处理出链上信息.
既然在树上,我们不如将重链看成这个链,而将链上的轻儿子的信息提前处理出来,在重链上的点的信息通过进行矩阵速推.
\]
(注: \(f'[x][0]\) 是点 \(x\) 只处理轻儿子和自己信息时的值)
那么如何修改?
同一条重链上点的修改对于这条链上的点是没有影响的,因为重链每个点只保存其轻儿子的信息,有影响的是对于这条链 \(top\) 的父亲节点,那么我们的思路就出来了,修改点时,修改链 \(top\) 父亲的矩阵信息即可;
总结一下思路: 首先将DP式子化为通项矩阵乘的形式,先处理出每个节点轻儿子的信息,在树上用树链剖分维护链上矩阵乘信息,修改时,每次修改链首父亲的矩阵值,修改至树根重链.修改细节即修改点信息,之后从链尾到链首矩阵乘,与之前的值作差修改.最后求答案时直接处理链上信息即可;
code
#include<bits/stdc++.h>
#define le(p) p<<1
#define re(p) p<<1|1
#define ll long long
using namespace std;
const int maxn=1e5+7,inf=1e9;
int n,m,head[maxn],son[maxn],top[maxn],bot[maxn],fl[maxn];
int fa[maxn],id[maxn],cent,tot,a[maxn],sz[maxn],f[maxn][2];
struct node{
int next,to;
}edge[maxn<<1];
struct matrix{
ll g[2][2];
matrix operator *(const matrix x) const{
matrix ans;
ans.g[0][0]=max(g[0][0]+x.g[0][0],g[0][1]+x.g[1][0]);
ans.g[0][1]=max(g[0][0]+x.g[0][1],g[0][1]+x.g[1][1]);
ans.g[1][0]=max(g[1][0]+x.g[0][0],g[1][1]+x.g[1][0]);
ans.g[1][1]=max(g[1][0]+x.g[0][1],g[1][1]+x.g[1][1]);
return ans;
}//矩阵乘
}tr[maxn<<2];
matrix ori[maxn];
template<typename type_of_scan>
inline void scan(type_of_scan &x){
type_of_scan f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
template<typename top,typename... tops>
inline void scan(top &x,tops&... X){
scan(x),scan(X...);
}
inline void add(int u,int v){
edge[++cent]=(node){head[u],v};head[u]=cent;
edge[++cent]=(node){head[v],u};head[v]=cent;
}
//树链剖分预处理
void dfs1(int x){
sz[x]=1;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]) continue;
fa[y]=x,dfs1(y);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) son[x]=y;
}
}
void dfs2(int x,int tp){
id[x]=++tot,fl[tot]=x;top[x]=tp;
if(son[x]) dfs2(son[x],tp),bot[x]=bot[son[x]];
else return bot[x]=x,void();
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
//树上DP
void dfs3(int x){
f[x][0]=0;f[x][1]=a[x];
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]) continue;
dfs3(y);
f[x][0]+=max(f[y][0],f[y][1]);
f[x][1]+=f[y][0];
}
}
void push_up(int p){
tr[p]=tr[le(p)]*tr[re(p)];
}
//建树时每个点只保存自己和轻儿子的信息
void build(int l,int r,int p){
if(l==r){
tr[p].g[0][0]=tr[p].g[0][1]=0;tr[p].g[1][1]=-inf;
tr[p].g[1][0]=a[fl[l]];int x=fl[l];
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]||y==son[x]) continue;
tr[p].g[0][0]+=max(f[y][0],f[y][1]);
tr[p].g[1][0]+=f[y][0];
}
tr[p].g[0][1]=tr[p].g[0][0];
ori[fl[l]]=tr[p];
return ;
}
int mid=l+r>>1;
build(l,mid,le(p));build(mid+1,r,re(p));
push_up(p);
}
void add(int x,int l,int r,int p){
if(l==r) return tr[p]=ori[fl[l]],void();
int mid=l+r>>1;
if(x<=mid) add(x,l,mid,le(p));
else add(x,mid+1,r,re(p));
push_up(p);
}
matrix query(int nl,int nr,int l,int r,int p){
if(nl<=l&&r<=nr) return tr[p];
int mid=l+r>>1;
if(nl<=mid&&mid<nr) return query(nl,nr,l,mid,le(p))*query(nl,nr,mid+1,r,re(p));
else if(nl<=mid) return query(nl,nr,l,mid,le(p));
else return query(nl,nr,mid+1,r,re(p));
}
matrix query(int x){
return query(id[top[x]],id[bot[x]],1,n,1);
}
matrix solve(int x,int k){
ori[x].g[1][0]+=k-a[x];a[x]=k;
while(x){
matrix old,news;
old=query(top[x]);//没修改的
add(id[x],1,n,1);//change
news=query(top[x]);//修改过的
x=fa[top[x]];if(!x) break;
ori[x].g[0][0]+=max(news.g[0][0],news.g[1][0])-max(old.g[0][0],old.g[1][0]);
ori[x].g[0][1]=ori[x].g[0][0];
ori[x].g[1][0]+=news.g[0][0]-old.g[0][0];
}
return query(1);
}
int main(){
scan(n,m);
for(int i=1;i<=n;i++) scan(a[i]);
for(int i=1,u,v;i<=n-1;i++)
scan(u),scan(v),add(u,v);
dfs1(1),dfs2(1,1),dfs3(1);build(1,n,1);
while(m--){
int x,k;
scan(x,k);
matrix ans=solve(x,k);
printf("%d\n",max(ans.g[0][0],ans.g[1][0]));
}
return 0;
}
例题
[NOIP2018]保卫王国;
有两个点,强制选点或强制不选,求最小覆盖集;
最小覆盖集=全集-最大权独立集;
强制选点或不选可以通过将其改为 \(-inf\) 和 \(inf\) ,之后就是D-DP模板了;
code
#include<bits/stdc++.h>
#define ll long long
#define le(x) x<<1
#define re(x) x<<1|1
using namespace std;
const int maxn=1e5+7;
const ll inf=1e9;
int n,m,fa[maxn],id[maxn],fl[maxn],sz[maxn],head[maxn];
int son[maxn],top[maxn],cent,tot,bot[maxn];
ll f[maxn][2],a[maxn],sum,ans;
vector<int>to[maxn];
char type[2];
struct node{
int next,to;
}edge[maxn<<1];
struct matrix{
ll g[2][2];
matrix operator *(const matrix x) const{
matrix ans;
ans.g[0][0]=max(g[0][0]+x.g[0][0],g[0][1]+x.g[1][0]);
ans.g[0][1]=max(g[0][0]+x.g[0][1],g[0][1]+x.g[1][1]);
ans.g[1][0]=max(g[1][0]+x.g[0][0],g[1][1]+x.g[1][0]);
ans.g[1][1]=max(g[1][0]+x.g[0][1],g[1][1]+x.g[1][1]);
return ans;
}
}tr[maxn<<3];
matrix ori[maxn];
template<typename type_of_scan>
inline void scan(type_of_scan &x){
type_of_scan f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
template<typename top,typename... tops>
inline void scan(top &x,tops&... X){
scan(x),scan(X...);
}
inline void add(int u,int v){
edge[++cent]=(node){head[u],v};head[u]=cent;
edge[++cent]=(node){head[v],u};head[v]=cent;
to[u].push_back(v),to[v].push_back(u);
}
void dfs1(int x){
sz[x]=1;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]) continue;
fa[y]=x;dfs1(y);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) son[x]=y;
}
}
void dfs2(int x,int tp){
id[x]=++tot,fl[tot]=x,top[x]=tp;
if(son[x]) dfs2(son[x],tp),bot[x]=bot[son[x]];
else return bot[x]=x,void();
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
void dfs3(int x){
f[x][1]=a[x],f[x][0]=0;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]) continue;
dfs3(y);
f[x][0]+=max(f[y][0],f[y][1]);
f[x][1]+=f[y][0];
}
}
void push_up(int p){
tr[p]=tr[le(p)]*tr[re(p)];
}
void build(int l,int r,int p){
if(l==r){
tr[p].g[0][0]=tr[p].g[0][1]=0;int x=fl[l];
tr[p].g[1][0]=a[fl[l]];tr[p].g[1][1]=-inf;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa[x]||y==son[x]) continue;
tr[p].g[0][0]+=max(f[y][0],f[y][1]);
tr[p].g[1][0]+=f[y][0];
}
tr[p].g[0][1]=tr[p].g[0][0];
ori[fl[l]]=tr[p];
return ;
}
int mid=l+r>>1;
build(l,mid,le(p)),build(mid+1,r,re(p));
push_up(p);
}
void add(int x,int l,int r,int p){
if(l==r) return tr[p]=ori[fl[l]],void();
int mid=l+r>>1;
if(x<=mid) add(x,l,mid,le(p));
else add(x,mid+1,r,re(p));
push_up(p);
}
matrix query(int nl,int nr,int l,int r,int p){
if(nl<=l&&r<=nr) return tr[p];
int mid=l+r>>1;
if(nl<=mid&&mid<nr) return query(nl,nr,l,mid,le(p))*query(nl,nr,mid+1,r,re(p));
else if(nl<=mid) return query(nl,nr,l,mid,le(p));
else return query(nl,nr,mid+1,r,re(p));
}
matrix query(int x){
return query(id[top[x]],id[bot[x]],1,n,1);
}
void modify(int x,ll k){
ori[x].g[1][0]+=k-a[x];a[x]=k;
while(x){
matrix old,news;
old=query(top[x]);add(id[x],1,n,1);
news=query(top[x]);
x=fa[top[x]];if(!x) break;
ori[x].g[0][0]+=max(news.g[0][0],news.g[1][0])-max(old.g[0][0],old.g[1][0]);
ori[x].g[0][1]=ori[x].g[0][0];
ori[x].g[1][0]+=news.g[0][0]-old.g[0][0];
}
return ;
}
int main(){
scan(n,m);cin>>type;
for(int i=1;i<=n;i++) scan(a[i]),sum+=a[i];
for(int i=1,u,v;i<=n-1;i++)
scan(u),scan(v),add(u,v);
for(int i=1;i<=n;i++) sort(to[i].begin(),to[i].end());
dfs1(1),dfs2(1,1),dfs3(1),build(1,n,1);
while(m--){
int f1,f2,g1,g2,t1,t2;
scan(f1,f2,g1,g2);
if(f2==g2&&!f2){
vector<int>::iterator pos=lower_bound(to[f1].begin(),to[f1].end(),g1);
if(pos!=to[f1].end()&&(*pos)==g1){
puts("-1");continue;
}
}
t1=a[f1],t2=a[g1];
modify(f1,f2?-inf:inf);
modify(g1,g2?-inf:inf);
matrix ol=query(1);
ans=max(ol.g[0][0],ol.g[1][0]);
if(!f2) ans=ans-inf+t1;
if(!g2) ans=ans-inf+t2;
printf("%lld\n",sum-ans);
modify(f1,t1),modify(g1,t2);
}
return 0;
}
当然还有倍增做法,但这里不再讲述,D-DP中一个重要思想就是讲DP式子变成矩阵乘的形式,用数据结构进行维护,其高效并支持拓展;
D-DP的更多相关文章
- BZOJ 1911: [Apio2010]特别行动队 [斜率优化DP]
1911: [Apio2010]特别行动队 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 4142 Solved: 1964[Submit][Statu ...
- 2013 Asia Changsha Regional Contest---Josephina and RPG(DP)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=4800 Problem Description A role-playing game (RPG and ...
- AEAI DP V3.7.0 发布,开源综合应用开发平台
1 升级说明 AEAI DP 3.7版本是AEAI DP一个里程碑版本,基于JDK1.7开发,在本版本中新增支持Rest服务开发机制(默认支持WebService服务开发机制),且支持WS服务.RS ...
- AEAI DP V3.6.0 升级说明,开源综合应用开发平台
AEAI DP综合应用开发平台是一款扩展开发工具,专门用于开发MIS类的Java Web应用,本次发版的AEAI DP_v3.6.0版本为AEAI DP _v3.5.0版本的升级版本,该产品现已开源并 ...
- BZOJ 1597: [Usaco2008 Mar]土地购买 [斜率优化DP]
1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4026 Solved: 1473[Submit] ...
- [斜率优化DP]【学习笔记】【更新中】
参考资料: 1.元旦集训的课件已经很好了 http://files.cnblogs.com/files/candy99/dp.pdf 2.http://www.cnblogs.com/MashiroS ...
- BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 9812 Solved: 3978[Submit][St ...
- px、dp和sp,这些单位有什么区别?
DP 这个是最常用但也最难理解的尺寸单位.它与“像素密度”密切相关,所以 首先我们解释一下什么是像素密度.假设有一部手机,屏幕的物理尺寸为1.5英寸x2英寸,屏幕分辨率为240x320,则我们可以计算 ...
- android px转换为dip/dp
/** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public int dipTopx(Context context, float dpValue) { final floa ...
- POJ 3254. Corn Fields 状态压缩DP (入门级)
Corn Fields Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 9806 Accepted: 5185 Descr ...
随机推荐
- Maven+Spring 框架,ModelAndView在页面取值不成功
如果创建的是maven project , maven生成的web.xml是这样的: 但是这样是不对的,应该修改成: 下面是代码: <?xml version="1.0" e ...
- LeetCode448-数组中消失的数字
题目 给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次. 找到所有在 [1, n] 范围之间没有出现在数组中的数字. 您能 ...
- python学习笔记 | 列表去重
''' @author: 人人都爱小雀斑 @time: 2020/3/10 10:29 @desc: ''' L=[1,5,7,4,6,3,0,5,8,4,4] 方法1:for循环 L1=[] for ...
- Linux三剑客grep、awk和sed
grep,sed 和 awk是Linux/Unix 系统中常用的三个文本处理的命令行工具,称为文本处理三剑客.本文将简要介绍这三个命令并给出基本用法. 管道 在介绍这两个命令之前,有必要介绍一下Uni ...
- kernel升级模式RKS让人振奋
前几天刚将我的ERP内核从701_rel 升级到721_ext_rel,看到721的说明了讲到,对于这次的更新,支持RKS(Rolling Kernel Switch)了,简单的讲,就是以后对于内核的 ...
- Linux下nginx的安装以及环境配置
参考链接 https://blog.csdn.net/qq_42815754/article/details/82980326 环境: centos7 .nginx-1.9.14 1.下载 并解压 ...
- 1.8V转3V,1,8V转3.3V电源芯片的规格书参数
1.8V电平如何稳压稳定输出3V或者3.3V,就需要用到1.8V转3V,1,8V转3.3V电源芯片,就PW5100(低功耗,外围简单),PW5200A是可调输出电压,可以输出电压根据外围电阻来设置命令 ...
- centos7搭建dolphinscheduler集群
一.简述 Apache DolphinScheduler是一个分布式去中心化,易扩展的可视化DAG工作流任务调度系统.致力于解决数据处理流程中错综复杂的依赖关系,使调度系统在数据处理流程中开箱即用.有 ...
- Kubernetes调整Node节点快速驱逐pod的时间
在高可用的k8s集群中,当Node节点挂掉,kubelet无法提供工作的时候,pod将会自动调度到其他的节点上去,而调度到节点上的时间需要我们慎重考量,因为它决定了生产的稳定性.可靠性,更快的迁移可以 ...
- jackson学习之二:jackson-core
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...