zoukankan      html  css  js  c++  java
  • 【模板】"动态 DP"&动态树分治

    CLII.【模板】"动态 DP"&动态树分治

    裸的树上最大独立集怎么做?设 \(f_{x,0}\) 表示在 \(x\) 子树中, \(x\) 不选的最大答案;\(f_{x,1}\) 表示在 \(x\) 子树中,\(x\) 选的最大答案。则有

    \[f_{x,0}=\sum\limits_{y\in\text{son}_x}\max(f_{y,0},f_{y,1}) \]

    \[f_{x,1}=a_x+\sum\limits_{y\in\text{son}_x}f_{y,0} \]

    现在要支持修改,怎么办?

    发现,每次修改,DP值受到影响的位置是从修改位置到根的一条路径。听上去像是树剖应用的场景?

    于是我们考虑树剖,并设 \(g_{x,0/1}\) 表示忽略 \(x\) 的重儿子时的答案。则,\(f_{x,0}=\max(f_{y,0},f_{y,1})+g_{x,0},f_{x,1}=f_{y,0}+g_{x,1}\),其中 \(y\)\(x\) 的重儿子。

    现在,我们考虑将其转成矩阵的形式。则有

    \[\begin{bmatrix}f_{y,0}\\f_{y,1}\end{bmatrix}\begin{bmatrix}g_{x,0}&g_{x,1}\\g_{x,0}&-\infty\end{bmatrix}=\begin{bmatrix}f_{x,0}\\f_{x,1}\end{bmatrix} \]

    其中,矩阵乘法的定义是 \(uv=w\Leftrightarrow w_{i,j}=\max\limits_k\{u_{i,k}+v_{k,j}\}\)。手推可得其满足结合律。

    于是,我们考虑从一个叶子的 \(f\) 数组开始(令人惊喜的是,叶子节点的 \(f\)\(g\) 是相等的(因为其不存在儿子),这使得转移很方便),一路往上乘矩阵,即可得到该叶子所在重链上任意节点的 \(f\) 数组。并且,因为任意重链的结尾节点都是叶子,所以通过上述方法可以求出任意节点的 \(f\) 数组。

    现在考虑带修。发现,一次修改只会影响到根的路径上所有重链顶的父亲的 \(g\) 数组,而这样的父亲最多只有 \(O(\log n)\) 个,故直接暴力跳链修改父亲即可。

    时间复杂度 \(O(n\log^2n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=0xd0d0d0d0;
    int n,m,a[100100],f[100100][2],g[100100][2];//0:self unchosen 1:self chosen
    int fa[100100],son[100100],sz[100100],dfn[100100],rev[100100],top[100100],bot[100100],tot;
    vector<int>v[100100];
    void dfs1(int x){
    	sz[x]=1,f[x][0]=0,f[x][1]=a[x];
    	for(auto y:v[x])if(y!=fa[x]){
    		fa[y]=x,dfs1(y),sz[x]+=sz[y];
    		if(sz[son[x]]<sz[y])son[x]=y;
    		f[x][0]+=max(f[y][0],f[y][1]),f[x][1]+=f[y][0];
    	}
    }
    void dfs2(int x){
    	dfn[x]=++tot,rev[tot]=x;if(!top[x])top[x]=x;if(son[x])top[son[x]]=top[x],dfs2(son[x]);else bot[top[x]]=x;
    	for(auto y:v[x])if(y!=fa[x]&&y!=son[x])dfs2(y),g[x][0]+=max(f[y][0],f[y][1]),g[x][1]+=f[y][0];
    }
    #define lson x<<1
    #define rson x<<1|1
    #define mid ((l+r)>>1)
    struct Matrix{
    	int t[2][2];
    	Matrix(){memset(t,inf,sizeof(t));}
    	Matrix(int x){t[1][0]=t[0][1]=inf,t[0][0]=t[1][1]=0;}
    	int*operator[](const int&x){return t[x];}
    	friend Matrix operator*(Matrix u,Matrix v){Matrix w;for(int i=0;i<2;i++)for(int j=0;j<2;j++)for(int k=0;k<2;k++)w[i][j]=max(w[i][j],u[i][k]+v[k][j]);return w;}
    	void print()const{for(int i=0;i<2;i++,puts(""))for(int j=0;j<2;j++)printf("%d ",t[i][j]);}
    	int zero(){return max(max(t[0][0],t[1][0]),max(t[0][1],t[1][1]));}
    	int one(){return max(t[0][0],t[1][0]);}
    }seg[400100];
    Matrix Generate(int x){Matrix M;M[0][0]=g[x][0],M[1][0]=g[x][0],M[0][1]=g[x][1],M[1][1]=inf;return M;}
    void build(int x,int l,int r){if(l==r)seg[x]=Generate(rev[l]);else build(lson,l,mid),build(rson,mid+1,r),seg[x]=seg[rson]*seg[lson];}
    Matrix query(int x,int l,int r,int L,int R){if(l>R||r<L)return Matrix(0);if(L<=l&&r<=R)return seg[x];return query(rson,mid+1,r,L,R)*query(lson,l,mid,L,R);}
    void reset(int x,int l,int r,int P){if(l>P||r<P)return;if(l==r)seg[x]=Generate(rev[P]);else reset(lson,l,mid,P),reset(rson,mid+1,r,P),seg[x]=seg[rson]*seg[lson];}
    int modify(int x,int y){
    	Matrix M;
    	for(int i=x;top[i]!=1;)M=query(1,1,n,dfn[top[i]],dfn[bot[top[i]]]),i=fa[top[i]],g[i][0]-=M.zero(),g[i][1]-=M.one();
    	g[x][1]+=y-a[x],a[x]=y,reset(1,1,n,dfn[x]);
    	for(int i=x;top[i]!=1;)M=query(1,1,n,dfn[top[i]],dfn[bot[top[i]]]),i=fa[top[i]],g[i][0]+=M.zero(),g[i][1]+=M.one(),reset(1,1,n,dfn[i]);
    	M=query(1,1,n,1,dfn[bot[1]]);
    	return M.zero();
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]),g[i][1]=a[i];
    	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
    	dfs1(1),dfs2(1),build(1,1,n);
    	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),printf("%d\n",modify(x,y));
    	return 0;
    }
    

  • 相关阅读:
    代码审计之越权及越权
    代码审计之XSS及修复
    代码审计之SQL注入及修复
    代码审计之CSRF原理及修复
    挖穿各大SRC的短信轰炸
    Kerberoasting攻击
    SPN扫描
    Windows认证 | 域认证
    Windows认证 | 网络认证
    Ceph 纠删码介绍
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601766.html
Copyright © 2011-2022 走看看