动态DP

更新日志 2025/02/03:开工。

2025/02/04:大幅修正。

2025/02/09:重构,完工。


问题

动态DP通常用来解决动态更改数值并求DP值。

思路

首先我们常规地得出DP方程。

这并不能满足快速修改的需要。所以我们考虑储存状态转移信息,然后借助线段树维护整个区间的状态转移信息,这样我们就可以进行单点修改操作,并通过区间查询获取DP值。

如果转移信息不太好维护,考虑借助矩阵维护信息。这是一个常见的解决方案。

常见情景

动态DP通常用来解决若只的树上DP问题套上毒瘤的单点修改问题

通常来说,树型DP单点的修改往往会影响其到根节点的路径的DP值。

很显然的,我们考虑重链剖分,并对剖分之后的 dfn 序建立线段树。

在修改的时候,我们不断跳轻链,在每一条重链的第一次到达的节点更新信息,就完成了到根路径的修改。查询的话,通常情况下树型DP都是从下往上的,所以我们考虑当前节点到其所在重链的末尾节点的转移信息和(在dfn序下,是连续的一段区间),就是它的DP值。

这样复杂度是 \(O(n\log^2n)\) 的。

利用全局平衡二叉树可以做到 \(O(n\log n)\) 的复杂度,详见链接页面。

例题

P4719

代码
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef double db;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template <typename Type>
using vec=vector<Type>;
template <typename Type>
using grheap=priority_queue<Type>;
template <typename Type>
using lrheap=priority_queue<Type,vector<Type>,greater<Type> >;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define per(i,x,y) for(int i=(x);i>=(y);i--)
#define repl(i,x,y) for(int i=(x);i<(y);i++)

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7/*998244353*/;

const int N=1e5+5;

int n,m;
int a[N];

struct matrix{
    int m[2][2];
    matrix(){memset(m,-0x3f,sizeof(m));}
};
matrix operator*(matrix a,matrix b){
    matrix c;
    repl(i,0,2)repl(j,0,2)repl(k,0,2)chmax(c.m[i][j],a.m[i][k]+b.m[k][j]);
    return c;
}

matrix val[N];
struct segment{
    matrix dat[N<<2];
    void update(int x){
        dat[x]=dat[x<<1]*dat[x<<1|1];
    }
    void build(int x=1,int l=1,int r=n){
        if(l==r){
            dat[x]=val[l];
            return;
        }
        int m=l+r>>1;
        build(x<<1,l,m);
        build(x<<1|1,m+1,r);
        update(x);
    }
    void change(int k,int x=1,int l=1,int r=n){
        if(l==r){
            dat[x]=val[l];
            return;
        }
        int m=l+r>>1;
        if(k<=m)change(k,x<<1,l,m);
        else change(k,x<<1|1,m+1,r);
        update(x);
    }
    matrix query(int lq,int rq,int x=1,int l=1,int r=n){
        if(lq<=l&&r<=rq)return dat[x];
        int m=l+r>>1;
        if(lq<=m&&m<rq){
            return query(lq,rq,x<<1,l,m)*query(lq,rq,x<<1|1,m+1,r);
        }else{
            if(lq<=m)return query(lq,rq,x<<1,l,m);
            else return query(lq,rq,x<<1|1,m+1,r);
        }
    }
}seg;

vec<int> g[N];

int f[N][2];

int dcnt;
int dfn[N],siz[N],son[N];
int fa[N],tp[N],en[N];
void dfs1(int now,int fid){
    siz[now]=1;
    fa[now]=fid;
    for(auto nxt:g[now]){
        if(nxt==fid)continue;
        dfs1(nxt,now);
        siz[now]+=siz[nxt];
        if(siz[nxt]>siz[son[now]])son[now]=nxt;
    }
}
void dfs2(int now,int top){
    dfn[now]=++dcnt;
    tp[now]=top;
    val[dfn[now]].m[0][0]=f[now][0]=0;
    val[dfn[now]].m[1][0]=f[now][1]=a[now];
    val[dfn[now]].m[0][1]=0;
    if(son[now]){
        dfs2(son[now],tp[now]);
        en[now]=en[son[now]];
        f[now][0]+=max(f[son[now]][0],f[son[now]][1]);
        f[now][1]+=f[son[now]][0];
    }else en[now]=now;
    for(auto nxt:g[now]){
        if(nxt==fa[now]||nxt==son[now])continue;
        dfs2(nxt,nxt);
        f[now][0]+=max(f[nxt][0],f[nxt][1]);
        f[now][1]+=f[nxt][0];
        val[dfn[now]].m[0][1]=(val[dfn[now]].m[0][0]+=max(f[nxt][0],f[nxt][1]));
        val[dfn[now]].m[1][0]+=f[nxt][0];
    }
}

void changepath(int x,int y){
    val[dfn[x]].m[1][0]+=y-a[x];a[x]=y;
    matrix fst,snd;
    while(1){
        fst=seg.query(dfn[tp[x]],dfn[en[x]]);
        seg.change(dfn[x]);
        snd=seg.query(dfn[tp[x]],dfn[en[x]]);
        x=fa[tp[x]];
        if(!x)break;
        val[dfn[x]].m[0][1]=(val[dfn[x]].m[0][0]+=max(snd.m[0][0],snd.m[1][0])-max(fst.m[0][0],fst.m[1][0]));
        val[dfn[x]].m[1][0]+=snd.m[0][0]-fst.m[0][0];
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    rep(i,1,n)cin>>a[i];
    rep(i,1,n-1){
        int a,b;cin>>a>>b;
        g[a].pub(b);g[b].pub(a);
    }
    dfs1(1,0);dfs2(1,1);
    seg.build();
    rep(i,1,m){
        int x,y;
        cin>>x>>y;
        changepath(x,y);
        matrix res=seg.query(dfn[1],dfn[en[1]]);
        cout<<max(res.m[0][0],res.m[1][0])<<"\n";
    }
    return 0;
}
posted @   LastKismet  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示