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());
    	}
    }
    
    
    
    
  • 相关阅读:
    opencv下载,安装教程
    VS2010中程序编译生成都是正确的,直接生成的exe也是正确的,就是在VS中运行出现错误
    Oracle11g em启动报此网站的安全证书有问题的解决方案
    Python教程百度云资源分享,全套,完整版!!!
    Python视频教程免费分享(2020年最新版)
    Python 环境搭建(Win 安装以及Mac OS 安装)
    Python学习路线图(2020年最新版)
    Python爬虫入门教程之BeautifulSoup
    Python数据分析练手:分析知乎大V
    python 可变对象与不可变对象
  • 原文地址:https://www.cnblogs.com/birchtree/p/12663149.html
Copyright © 2011-2022 走看看