zoukankan      html  css  js  c++  java
  • [LuoguP4719][模板]动态DP(动态DP)

    [LuoguP4719][模板]动态DP(动态DP)

    题面

    给出一棵(n)个点的树,点带权。(m)组修改,每次修改一个点的点权,并询问整棵树最大权独立集大小。

    分析

    约定:(child(x))表示(x)的儿子集合,(son(x))表示(x)的重儿子。
    先写出树形DP.设(f_{x,0/1})表示不选或选(x),(x)的子树里最大权独立集的大小.
    如果不选(x),那么儿子(y)可以任意选

    [f_{x,0}=sum_{y in child(x)} max(f_{y,0},f_{y,1}) ]

    如果选了(x),那么儿子(y)就不能选。

    [f_{x,1}=val_x+sum_{y in child(x)} f_{y,0} ]

    动态DP的套路,轻链和重链分别维护,令:

    [g_{x,0}=sum_{y in child(x)-{son(x)}}max(f_{y,0},f_{y,1}) ]

    [g_{x,1}=val_x+sum_{y in child(x)-{son(x)}}f_{y,0} ]

    那么有:

    (f_{x,0}=max(f_{son(x),0},f_{son(x),1})+g_{x,0})
    (f_{x,1}=f_{son(x),0}+g_{x,1})

    写成矩阵的形式(注意这里是max,+矩阵乘法)

    [egin{bmatrix}f_{x,0} \ f_{x,1} end{bmatrix}=egin{bmatrix}g_{x,0} g_{x,0} \ g_{x,1} -infty end{bmatrix} egin{bmatrix}f_{son(x),0} \ f_{son(x),1} end{bmatrix} ]

    然后按照动态DP的套路,沿着重链往上跳,修改链头尾节点即可。对于路径查询矩阵乘积,可以用树链剖分+线段树或LCT实现。

    代码

    树链剖分+线段树:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define INF 0x3f3f3f3f
    #define maxn 200000
    using namespace std;
    typedef long long ll;
    int n,m;
    struct edge {
    	int from;
    	int to;
    	int next;
    } E[maxn*2+5];
    int head[maxn+5];
    int esz=1;
    void add_edge(int u,int v) {
    	esz++;
    	E[esz].from=u;
    	E[esz].to=v;
    	E[esz].next=head[u];
    	head[u]=esz;
    }
    int fa[maxn+5],son[maxn+5],sz[maxn+5],top[maxn+5],btm[maxn+5]/*所在重链最底端*/,dfn[maxn+5],hash_dfn[maxn+5];
    void dfs1(int x,int f) {
    	sz[x]=1;
    	fa[x]=f;
    	for(int i=head[x]; i; i=E[i].next) {
    		int y=E[i].to;
    		if(y!=f) {
    			dfs1(y,x);
    			sz[x]+=sz[y];
    			if(sz[y]>sz[son[x]]) son[x]=y;
    		}
    	}
    }
    int tim=0;
    void dfs2(int x,int t) {
    	top[x]=t;
    	dfn[x]=++tim;
    	hash_dfn[dfn[x]]=x;
    	if(son[x]) {
    		dfs2(son[x],t);
    		btm[x]=btm[son[x]];//维护重链最底端节点
    	} else btm[x]=x;
    	for(int i=head[x]; i; i=E[i].next) {
    		int y=E[i].to;
    		if(y!=fa[x]&&y!=son[x]) {
    			dfs2(y,y);
    		}
    	}
    }
    
    
    struct matrix {
    	ll a[2][2];
    	inline void set(int x) {
    		for(int i=0; i<2; i++) {
    			for(int j=0; j<2; j++) a[i][j]=x;
    		}
    	}
    	friend matrix operator * (matrix p,matrix q) {
    		matrix ans;
    		ans.set(-INF);
    		for(int i=0; i<2; i++) {
    			for(int j=0; j<2; j++) {
    				for(int k=0; k<2; k++) {
    					ans.a[i][j]=max(ans.a[i][j],p.a[i][k]+q.a[k][j]);
    				}
    			}
    		}
    		return ans;
    	}
    } mat[maxn+5];
    ll val[maxn+5];
    ll f[maxn+5][2],g[maxn+5][2];
    void dfs3(int x) {
    	f[x][0]=0;
    	f[x][1]=val[x];
    	for(int i=head[x]; i; i=E[i].next) {
    		int y=E[i].to;
    		if(y!=fa[x]) {
    			dfs3(y);
    			f[x][0]+=max(f[y][0],f[y][1]);
    			f[x][1]+=f[y][0];
    		}
    	}
    	g[x][0]=0,g[x][1]=val[x];
    	for(int i=head[x]; i; i=E[i].next) {
    		int y=E[i].to;
    		if(y!=fa[x]&&y!=son[x]) {
    			g[x][0]+=max(f[y][0],f[y][1]);
    			g[x][1]+=f[y][0];
    		}
    	}
    	mat[x].a[0][0]=g[x][0];
    	mat[x].a[0][1]=g[x][0];
    	mat[x].a[1][0]=g[x][1];
    	mat[x].a[1][1]=-INF;
    }
    
    struct segment_tree {
    	struct node {
    		int l;
    		int r;
    		matrix v;
    	} tree[maxn*4+5];
    	void push_up(int pos) {
    		tree[pos].v=tree[pos<<1].v*tree[pos<<1|1].v;
    	}
    	void build(int l,int r,int pos) {
    		tree[pos].l=l;
    		tree[pos].r=r;
    		if(l==r) {
    			tree[pos].v=mat[hash_dfn[l]];
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(l,mid,pos<<1);
    		build(mid+1,r,pos<<1|1);
    		push_up(pos);
    	}
    	void update(int upos,matrix &uval,int pos) {
    		if(tree[pos].l==tree[pos].r) {
    			tree[pos].v=uval;
    			return;
    		}
    		int mid=(tree[pos].l+tree[pos].r)>>1;
    		if(upos<=mid) update(upos,uval,pos<<1);
    		else update(upos,uval,pos<<1|1);
    		push_up(pos);
    	}
    	matrix query(int L,int R,int pos) {
    		if(L<=tree[pos].l&&R>=tree[pos].r) return tree[pos].v;
    		int mid=(tree[pos].l+tree[pos].r)>>1;
    		matrix ans;
    		ans.a[0][0]=ans.a[1][1]=0;
    		ans.a[0][1]=ans.a[1][0]=-INF;
    		if(L<=mid) ans=ans*query(L,R,pos<<1);
    		if(R>mid) ans=ans*query(L,R,pos<<1|1);
    		return ans;
    	}
    } T;
    ll get_f(int x,int k) {
    	//f[x]需要从x所在重链底端推上来,变成区间矩阵乘法
    	return T.query(dfn[x],dfn[btm[x]],1).a[k][0];
    }
    void change(int x,int v) {
    	g[x][1]+=v-val[x];
    	val[x]=v;
    	while(x) {
    		mat[x].a[0][0]=g[x][0];
    		mat[x].a[0][1]=g[x][0];
    		mat[x].a[1][0]=g[x][1];
    		mat[x].a[1][1]=-INF;
    		T.update(dfn[x],mat[x],1);
    		x=top[x];
    		g[fa[x]][0]-=max(f[x][0],f[x][1]);
    		g[fa[x]][1]-=f[x][0];
    		f[x][0]=get_f(x,0);
    		f[x][1]=get_f(x,1);
    		g[fa[x]][0]+=max(f[x][0],f[x][1]);
    		g[fa[x]][1]+=f[x][0];
    		x=fa[x];
    	}
    }
    
    int main() {
    	int u,v;
    	scanf("%d %d",&n,&m);
    	for(int i=1; i<=n; i++) scanf("%lld",&val[i]);
    	for(int i=1; i<n; i++) {
    		scanf("%d %d",&u,&v);
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	dfs1(1,0);
    	dfs2(1,1);
    	dfs3(1);
    	T.build(1,n,1);
    	for(int i=1; i<=m; i++) {
    		scanf("%d %d",&u,&v);
    		change(u,v);
    		printf("%lld
    ",max(get_f(1,0),get_f(1,1)));
    	}
    }
    

    LCT:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define INF 0x3f3f3f3f
    #define maxn 200000
    using namespace std;
    typedef long long ll;
    int n,m;
    struct edge {
    	int from;
    	int to;
    	int next;
    } E[maxn*2+5];
    int head[maxn+5];
    int esz=1;
    void add_edge(int u,int v) {
    	esz++;
    	E[esz].from=u;
    	E[esz].to=v;
    	E[esz].next=head[u];
    	head[u]=esz;
    }
    
    struct matrix {
    	ll a[2][2];
    	matrix(){
    		a[0][0]=a[0][1]=a[1][0]=a[1][1]=-INF;
    	}
    	inline void set(int x) {
    		for(int i=0; i<2; i++) {
    			for(int j=0; j<2; j++) a[i][j]=x;
    		}
    	}
    	friend matrix operator * (matrix p,matrix q) {
    		matrix ans;
    		ans.set(-INF);
    		for(int i=0; i<2; i++) {
    			for(int j=0; j<2; j++) {
    				for(int k=0; k<2; k++) {
    					ans.a[i][j]=max(ans.a[i][j],p.a[i][k]+q.a[k][j]);
    				}
    			}
    		}
    		return ans;
    	}
    } mat[maxn+5];
    ll val[maxn+5];
    ll f[maxn+5][2],g[maxn+5][2];
    
    struct LCT {
    #define lson(x) (tree[x].ch[0])
    #define rson(x) (tree[x].ch[1])
    #define fa(x) (tree[x].fa)
    	struct node {
    		int ch[2];
    		int fa;
    		matrix v;
    	} tree[maxn+5];
    	inline bool is_root(int x) { //注意合并顺序
    		return !(lson(fa(x))==x||rson(fa(x))==x);
    	}
    	inline int check(int x) {
    		return rson(fa(x))==x;
    	}
    	void push_up(int x) {
    		tree[x].v=mat[x];
    		if(lson(x)) tree[x].v=tree[lson(x)].v*tree[x].v;
    		if(rson(x)) tree[x].v=tree[x].v*tree[rson(x)].v;
    	}
    	void rotate(int x) {
    		int y=tree[x].fa,z=tree[y].fa,k=check(x),w=tree[x].ch[k^1];
    		tree[y].ch[k]=w;
    		tree[w].fa=y;
    		if(!is_root(y)) tree[z].ch[check(y)]=x;
    		tree[x].fa=z;
    		tree[x].ch[k^1]=y;
    		tree[y].fa=x;
    		push_up(y);
    		push_up(x);
    	}
    	void splay(int x) {
    		while(!is_root(x)) {
    			int y=fa(x);
    			if(!is_root(y)) {
    				if(check(x)==check(y)) rotate(y);
    				else rotate(x);
    			}
    			rotate(x);
    		}
    	}
    	void access(int x) {
    		//access的时候可能由实变虚,或由虚变实,因此要更新f,g,方法类似LCT维护虚子树信息
    		//这里和树剖向上跳重链更新是类似的
    		if(x==2){
    			x=2;
    		}
    		for(int y=0; x; y=x,x=fa(x)) {
    			splay(x);
    			//原来的rson(x)由实变虚
    			if(rson(x)){
    				mat[x].a[0][0]+=max(tree[rson(x)].v.a[0][0],tree[rson(x)].v.a[1][0]);//这里也可以不用f和g,直接写对应矩阵里的值 
    				mat[x].a[1][0]+=tree[rson(x)].v.a[0][0];
    			} 
    			rson(x)=y;
    			if(rson(x)){
    				mat[x].a[0][0]-=max(tree[rson(x)].v.a[0][0],tree[rson(x)].v.a[1][0]);
    				mat[x].a[1][0]-=tree[rson(x)].v.a[0][0];
    			}
    			mat[x].a[0][1]=mat[x].a[0][0];
    			push_up(x);
    		}
    	}
    	void change(int x,int v) {
    		access(x);
    		splay(x);
    		mat[x].a[1][0]+=v-val[x];
    		push_up(x);
    		val[x]=v;
    	}
    	ll query(int x) {
    		splay(1);//查询前记得splay到根 
    		return max(tree[1].v.a[0][0],tree[1].v.a[1][0]);
    	}
    } T;
    
    void dfs(int x,int fa) {
    	f[x][0]=0;
    	f[x][1]=val[x];
    	for(int i=head[x]; i; i=E[i].next) {
    		int y=E[i].to;
    		if(y!=fa) {
    			dfs(y,x);
    			f[x][0]+=max(f[y][0],f[y][1]);
    			f[x][1]+=f[y][0];
    		}
    	}
    	mat[x].a[0][0]=mat[x].a[0][1]=f[x][0];//一开始全是轻边,f=g 
    	mat[x].a[1][0]=f[x][1];
    	mat[x].a[1][1]=-INF;
    	T.tree[x].v=mat[x];//初始化LCT
    	T.tree[x].fa=fa; //记得初始化fa
    }
    int main() {
    	int u,v;
    	scanf("%d %d",&n,&m);
    	for(int i=1; i<=n; i++) scanf("%lld",&val[i]);
    	for(int i=1; i<n; i++) {
    		scanf("%d %d",&u,&v);
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	dfs(1,0);
    	for(int i=1; i<=m; i++) {
    		scanf("%d %d",&u,&v);
    		T.change(u,v);
    		printf("%lld
    ",T.query(1));
    	}
    }
    

    全局平衡二叉树:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define INF 0x3f3f3f3f
    #define maxn 200000
    using namespace std;
    typedef long long ll;
    int n,m;
    struct edge {
    	int from;
    	int to;
    	int next;
    } E[maxn*2+5];
    int head[maxn+5];
    int esz=1;
    void add_edge(int u,int v) {
    	esz++;
    	E[esz].from=u;
    	E[esz].to=v;
    	E[esz].next=head[u];
    	head[u]=esz;
    }
    
    struct matrix {
    	ll a[2][2];
    	matrix() {
    		a[0][0]=a[0][1]=a[1][0]=a[1][1]=-INF;
    	}
    	inline void set(int x) {
    		for(int i=0; i<2; i++) {
    			for(int j=0; j<2; j++) a[i][j]=x;
    		}
    	}
    	friend matrix operator * (matrix p,matrix q) {
    		matrix ans;
    		ans.set(-INF);
    		for(int i=0; i<2; i++) {
    			for(int j=0; j<2; j++) {
    				for(int k=0; k<2; k++) {
    					ans.a[i][j]=max(ans.a[i][j],p.a[i][k]+q.a[k][j]);
    				}
    			}
    		}
    		return ans;
    	}
    	ll* operator [](int i) {
    		return a[i];
    	}
    } mat[maxn+5];
    ll val[maxn+5];
    ll f[maxn+5][2],g[maxn+5][2];
    int sz[maxn+5],lsz[maxn+5],son[maxn+5];
    
    void dfs1(int x,int fa) {
    	sz[x]=lsz[x]=1;
    	f[x][0]=0;
    	f[x][1]=val[x];
    	for(int i=head[x]; i; i=E[i].next) {
    		int y=E[i].to;
    		if(y!=fa) {
    			dfs1(y,x);
    			f[x][0]+=max(f[y][0],f[y][1]);
    			f[x][1]+=f[y][0];
    			sz[x]+=sz[y];
    			if(sz[son[x]]<sz[y]) son[x]=y;
    		}
    	}
    	g[x][0]=0,g[x][1]=val[x];
    	for(int i=head[x]; i; i=E[i].next) {
    		int y=E[i].to;
    		if(y!=fa&&y!=son[x]) {
    			g[x][0]+=max(f[y][0],f[y][1]);
    			g[x][1]+=f[y][0];
    			lsz[x]+=sz[y];
    		}
    	}
    	mat[x].a[0][0]=g[x][0];
    	mat[x].a[0][1]=g[x][0];
    	mat[x].a[1][0]=g[x][1];
    	mat[x].a[1][1]=-INF;
    }
    
    struct BST {
    #define fa(x) (tree[x].fa)
    #define lson(x) (tree[x].ch[0])
    #define rson(x) (tree[x].ch[1])
    	int root;
    	int tot;
    	int stk[maxn+5];//存储当前重链
    	int sumsz[maxn+5];//存储重链上点的lsz之和
    	struct node {
    		int fa;//全局平衡二叉树上的父亲
    		int ch[2];
    		matrix v;
    	} tree[maxn+5];
    	inline bool is_root(int x) { //注意合并顺序
    		return !(lson(fa(x))==x||rson(fa(x))==x);
    	}
    	void push_up(int x) {//很多函数和LCT是一样的
    		tree[x].v=mat[x];
    		if(lson(x)) tree[x].v=tree[lson(x)].v*tree[x].v;
    		if(rson(x)) tree[x].v=tree[x].v*tree[rson(x)].v;
    	}
    
    	int get_bst(int l,int r) {
    		if(l>r) return 0;
    		int mid=lower_bound(sumsz+l,sumsz+r+1,(sumsz[l-1]+sumsz[r])/2)-sumsz;//求带权重心
    		int x=stk[mid];
    		lson(x)=get_bst(l,mid-1);
    		rson(x)=get_bst(mid+1,r);//递归建树,这样的二叉树是平衡的
    		if(lson(x)) fa(lson(x))=x;//类似LCT,初始化fa和son 
    		if(rson(x)) fa(rson(x))=x;
    		push_up(x);
    		return x;
    	}
    	int build(int x,int f) {
    		int rt=0;
    		stk[++tot]=x;
    		sumsz[tot]+=lsz[x];
    		if(son[x]) { //继续dfs重链
    			sumsz[tot+1]+=sumsz[tot];
    			rt=build(son[x],x);
    		} else { //到了重链底部,可以建二叉树了
    			rt=get_bst(1,tot);
    			for(int i=1; i<=tot; i++) sumsz[i]=0;
    			tot=0;
    			return rt;
    		}
    		for(int i=head[x]; i; i=E[i].next) {
    			int y=E[i].to;
    			if(y!=f&&y!=son[x]) fa(build(y,x))=x;//对于轻链,递归下去建树,再用fa把它们连起来
    		}
    		return rt;
    	}
    	void update(int x) {
    		while(x) { //这一部分和树剖跳重链类似
    			int f=fa(x);
    			if(f&&is_root(x)) {//只有到了BST根的时候,说明已经处理完了整条重链,跳轻链到fa(x)更新上一条重链 
    				mat[f][0][0]-=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][0][1]-=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][1][0]-=tree[x].v[0][0];
    			}
    			push_up(x);
    			if(f&&is_root(x)) {
    				mat[f][0][0]+=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][0][1]+=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][1][0]+=tree[x].v[0][0];
    			}
    			x=fa(x);
    		}
    	}
    	void ini(){
    		dfs1(1,0);
    		root=build(1,0);
    	}
    	void change(int x,int v) {
    		mat[x][1][0]+=v-val[x];
    		val[x]=v;
    		update(x);
    	}
    	ll query(){
    		return max(tree[root].v[0][0],tree[root].v[1][0]);
    	}
    	void debug(){ 
    		printf("root=%d
    ",root); 
    		for(int i=1;i<=n;i++) printf("%d ",fa(i));
    		printf("
    ");
    	}
    } T;
    
    
    int main() {
    	int u,v;
    	scanf("%d %d",&n,&m);
    	for(int i=1; i<=n; i++) scanf("%lld",&val[i]);
    	for(int i=1; i<n; i++) {
    		scanf("%d %d",&u,&v);
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	T.ini();
    //	T.debug();
    	for(int i=1; i<=m; i++) {
    		scanf("%d %d",&u,&v);
    		T.change(u,v);
    		printf("%lld
    ",T.query());
    	}
    }
    
    
    
    
  • 相关阅读:
    【leetcode】416. Partition Equal Subset Sum
    【leetcode】893. Groups of Special-Equivalent Strings
    【leetcode】892. Surface Area of 3D Shapes
    【leetcode】883. Projection Area of 3D Shapes
    【leetcode】140. Word Break II
    【leetcode】126. Word Ladder II
    【leetcode】44. Wildcard Matching
    【leetcode】336. Palindrome Pairs
    【leetcode】354. Russian Doll Envelopes
    2017.12.22 英语面试手记
  • 原文地址:https://www.cnblogs.com/birchtree/p/12663149.html
Copyright © 2011-2022 走看看