• [模板] 动态dp


    用途

    对于某些树形dp(目前只会树上最大权独立集或者类似的),动态地修改点权,并询问修改后的dp值

    做法(树剖版)

    以最大权独立集为例

    设$f[x][0/1]$表示x选不选,这棵子树的最大权独立集大小

    那么有(设y是x的孩子)

    $$f[x][0]=sum{max{f[y][0],f[y][1]}} , f[x][1]=val[x]+sum{f[y][0]}$$

    那么在只关心其中的一个孩子y'的情况下,我们可以得到方程

    $$f[x][0]=S_0+max{f[y'][0],f[y'][1]},f[x][1]=S_1+f[y'][0]$$

    $S_0$和$S_1$的值参照上面的方程,它是与$f[y'][]$无关的

    这样的话,我们修改$f[x][]$的值,这个转移的方程不会变 但是这并没有什么卵用

    考虑用矩阵优化这个转移,先稍微变化一下转移的形式:

    $$f[x][0]=max{S_0+f[y'][0],S_0+f[y'][1]},f[x][1]=max{S_1+f[y'][0],-inf+f[y'][1]}$$

    然后我们发现,如果把矩阵乘法定义中的*变成+,+变成取max(即$c[i,j]=max{a[i,k]+b[k,j]}$),就可以把这个式子套进去

    (这样做是有道理的,因为max和+满足交换律、结合律,max满足加法分配率)

    就是说,x从y转移可以这样:

    $$(f[x][0],f[x][1])=(f[y][0],f[y][1])* left( egin{matrix} S_0 & S_1 \ S_0 & -inf end{matrix} ight) $$

    然而各种孩子们变来变去的,并不能直接用这个

    考虑用树剖来做:设$g[x]$为从x的重儿子转移到x的矩阵,为了方便,直接设$g[x][0]=S_0,g[x][1]=S_1$

    这样的话,我修改一个点的f值,它的实父亲(?)的g值是不会变的

    就是说,改的时候,只有到根的每条链的链顶的父亲的g值会改变(当然x自己的也会改变)

    这个变是怎么变的呢,就是

    $$g[x][0]+=max{f_{new}[y][0],f_{new}[y][1]}-max{f_{old}[y][0],f_{old}[y][1]} , g[x][1]+=f_{new}[y][0]-f_{old}[y][0] $$

    (y是x的轻儿子)

    那么我们改值的一个过程就可以写成这样:

      1.求出top[x]原来的f值

      2.修改x的g值

      3.求出top[x]现在的f值

      4.x=top[x]

    然后我们发现,叶节点的g值其实就是它的f值,所以我们求一个点的f值的时候直接把矩阵从叶节点乘到这个点就可以了

    最后的答案就是根节点的f值取个max

    复杂度$O(mlog^2n$),我的常数好大啊

    附代码(luogu4719)

      1 #include<bits/stdc++.h>
      2 #define CLR(a,x) memset(a,x,sizeof(a))
      3 using namespace std;
      4 typedef long long ll;
      5 typedef unsigned long long ull;
      6 typedef pair<int,int> pa;
      7 const int maxn=1e5+10,inf=0x3f3f3f3f;
      8 
      9 inline ll rd(){
     10     ll x=0;char c=getchar();int neg=1;
     11     while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();}
     12     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
     13     return x*neg;
     14 }
     15 
     16 struct Mat{
     17     int n,m,a[3][3];
     18     Mat(int x0=0,int x1=0,int x2=0,int x3=0,int x4=0,int x5=0){
     19         n=x0,m=x1,a[1][1]=x2,a[1][2]=x3,a[2][1]=x4,a[2][2]=x5;
     20     }
     21 }trans[maxn],g[maxn<<2]; //从它的重儿子转移到它
     22 inline Mat operator *(Mat a,Mat b){
     23     if(a.n==0) return b;
     24     if(b.n==0) return a;
     25     Mat re=Mat(a.n,b.m,-inf,-inf,-inf,-inf);
     26     for(int i=1;i<=re.n;i++){
     27         for(int j=1;j<=re.m;j++){
     28             for(int k=1;k<=a.m;k++){
     29                 re.a[i][j]=max(re.a[i][j],a.a[i][k]+b.a[k][j]);
     30             }
     31         }
     32     }return re;
     33 }
     34 
     35 int N,M,eg[maxn*2][2],egh[maxn],ect,val[maxn];
     36 int fa[maxn],dep[maxn],dfn[maxn],tot,siz[maxn],wson[maxn],id[maxn],bot[maxn];
     37 int f[maxn][2],top[maxn];
     38 
     39 inline void adeg(int a,int b){
     40     eg[++ect][0]=b,eg[ect][1]=egh[a],egh[a]=ect;
     41 }
     42 
     43 void dfs1(int x){
     44     f[x][0]=0,f[x][1]=val[x];
     45     siz[x]=1;
     46     for(int i=egh[x];i;i=eg[i][1]){
     47         int b=eg[i][0];if(b==fa[x]) continue;
     48         fa[b]=x,dep[b]=dep[x]+1;
     49         dfs1(b);siz[x]+=siz[b];
     50         if(siz[b]>siz[wson[x]]) wson[x]=b;
     51         f[x][0]+=max(f[b][0],f[b][1]);f[x][1]+=f[b][0];
     52     }
     53     int s=f[x][0]-max(f[wson[x]][0],f[wson[x]][1]);
     54     int m=f[x][1]-f[wson[x]][0];
     55     trans[x]=Mat(2,2,s,m,s,-inf);
     56 }
     57 
     58 void dfs2(int x){
     59     dfn[x]=++tot;id[tot]=x;
     60     top[x]=(x==wson[fa[x]])?top[fa[x]]:x;
     61     if(wson[x]) dfs2(wson[x]);
     62     else bot[top[x]]=x;
     63     for(int i=egh[x];i;i=eg[i][1]){
     64         int b=eg[i][0];if(b==fa[x]||b==wson[x]) continue;
     65         dfs2(b);
     66     }
     67 }
     68 
     69 inline void build(int p,int l,int r){
     70     if(l==r) g[p]=trans[id[l]];
     71     else{
     72         int m=l+r>>1;
     73         build(p<<1,l,m);build(p<<1|1,m+1,r);
     74         g[p]=g[p<<1|1]*g[p<<1];
     75     }
     76 }
     77 
     78 inline void change(int p,int l,int r,int x,int d0,int d1){
     79     if(l==r){
     80         g[p].a[1][1]+=d0,g[p].a[2][1]+=d0,g[p].a[1][2]+=d1;
     81     }else{
     82         int m=l+r>>1;
     83         if(x<=m) change(p<<1,l,m,x,d0,d1);
     84         else change(p<<1|1,m+1,r,x,d0,d1);
     85         g[p]=g[p<<1|1]*g[p<<1];
     86     }
     87 }
     88 
     89 inline Mat query(int p,int l,int r,int x,int y){
     90     if(x<=l&&r<=y) return g[p];
     91     else{
     92         int m=l+r>>1;Mat re=Mat(0);
     93         if(y>=m+1) re=query(p<<1|1,m+1,r,x,y);
     94         if(x<=m) re=re*query(p<<1,l,m,x,y);
     95         return re;
     96     }
     97 }
     98 
     99 inline int update(int x,int y){
    100     Mat od,nw;
    101     while(x){
    102         int a,b;
    103         if(y) a=0,b=y,y=0;
    104         else{
    105             a=max(nw.a[1][1],nw.a[1][2])-max(od.a[1][1],od.a[1][2]);
    106             b=nw.a[1][1]-od.a[1][1];
    107         }
    108         od=query(1,1,N,dfn[top[x]],dfn[bot[top[x]]]);
    109         change(1,1,N,dfn[x],a,b);
    110         nw=query(1,1,N,dfn[top[x]],dfn[bot[top[x]]]);
    111         x=fa[top[x]];
    112     }
    113     return max(nw.a[1][1],nw.a[1][2]);
    114 }
    115 
    116 int main(){
    117     //freopen("","r",stdin);
    118     int i,j,k;
    119     N=rd(),M=rd();
    120     for(i=1;i<=N;i++)
    121         val[i]=rd();
    122     for(i=1;i<N;i++){
    123         int a=rd(),b=rd();
    124         adeg(a,b);adeg(b,a);
    125     }
    126     dep[1]=1;dfs1(1);
    127     dfs2(1);build(1,1,N);
    128     for(i=1;i<=M;i++){
    129         int a=rd(),b=rd();
    130         printf("%d
    ",update(a,b-val[a]));
    131         val[a]=b;
    132     }
    133     return 0;
    134 }
  • 相关阅读:
    SQL Server:创建索引视图
    Asp.Net常用函数
    SQL Server联机丛书:删除存储过程
    音乐知识全接触
    深入透析样式表滤镜
    有一天,爸妈会变老
    今天终于买到票啦~~
    今天,回到上海啦~~(附工作生涯回顾)
    十八问:怎么才是喜欢编程
    把旧光驱改CD播放机的方法
  • 原文地址:https://www.cnblogs.com/Ressed/p/9967237.html
Copyright © 2020-2023  润新知