[P4719]【模板】动态DP

22 篇文章 0 订阅
11 篇文章 0 订阅

动态DP

也就是lg上面的拿到模板题。

题解

如果是直接求权值最大独立集的话,显然,我们有一个 O ( n ) O(n) O(n)树形dp方法。
但如果带修改的话我们每次都跑一条链明显是不合适的, O ( n q ) O(nq) O(nq)这T飞吧

考虑每次修改会影响那些地方的dp值,很明显,只会对这个点到根的这条链上产生影响,但如果我们找重心什么的再去对这条链进行修改是明显不行的。
我们得想个办法使得每次的修改次数更少。
首先,我们就想到了将dp的转移换成矩阵,这样每个位置的更改会独立出来,对于一条链上,它只会对它自身产生影响。
由于我们的dp式子是:
f u , 0 = ∑ max ⁡ ( f v , 0 , f v , 1 ) , f u , 1 = ∑ f v , 1 + a u f_{u,0}=\sum \max(f_{v,0},f_{v,1}),f_{u,1}=\sum f_{v,1}+a_{u} fu,0=max(fv,0,fv,1),fu,1=fv,1+au
很明显,每次修改的时候,对于f的影响只有其中的一个量会改变,我们修改时只用修改这一个量。
而上面的dp式子是可以通过矩阵进一步快速求出的。
当然们这里的矩阵不是原来的矩阵需要用 c i , j = max ⁡ ( a i , k + b k , j ) c_{i,j}=\max{(a_{i,k}+b_{k,j})} ci,j=max(ai,k+bk,j)的形式,其实只需要将原来的计算方法改一下就可以了,很明显,改成这样后依旧是符合矩阵运算的结合律的。
g u , 0 g_{u,0} gu,0表示点 u u u的所有儿子的 m a x ( f v , 0 , f v , 1 ) max(f_{v,0},f_{v,1}) max(fv,0,fv,1)之和, g u , 1 g_{u,1} gu,1表示其所有儿子的 g v , 0 g_{v,0} gv,0之和加上 a u a_{u} au,新的转移方式如下:
∣ f u , 0 f u , 1 ∣ = ∣ g u , 0 , g u , 0 g u , 0 , − ∞ ∣ ∣ f v , 0 f v , 1 ∣ \left| \begin{array}{cc} f_{u,0}\\f_{u,1} \end{array} \right |= \left| \begin{array}{cc} g_{u,0},g_{u,0}\\g_{u,0},-\infty \end{array} \right | \left| \begin{array}{cc} f_{v,0}\\f_{v,1} \end{array} \right | fu,0fu,1=gu,0,gu,0gu,0,fv,0fv,1
其中 v v v表示 u u u的重儿子。
由于每次修改维护的矩阵明显是在一个线段树上维护的,我们就将树链剖分的每条重链维护一棵线段树,转移就从重儿子转移到父亲。
由于每个点开始的重链一定是都可以走到叶子节点的,所以将它到它重链叶子节点的所有矩阵乘起来得到的答案就是它子树内的答案。

接下来修改的方式就很简单了。
很明显,对于跟它同在一条重链上的其它节点,它是不会影响它们的转移矩阵的,这些节点记录的是它轻儿子的状态,与它这个重儿子无关。它所影响到的只有与它与根节点的路径上,轻重链相交的部分,这些节点记录了它的状态。
对于这些部分,由于我们对它求的是和,可以减去后再加上,不需要知道其它位置的答案,修改一个点是 O ( l o g   n ) O(log\,n) O(logn)的,总共要修改 l o g   n log\,n logn个点,每次修改操作就是 O ( l o g 2   n ) O(log^2\,n) O(log2n)的。
求答案时只需要对根节点所在的重链进行一次求和。

总时间复杂度 O ( n l o g 2   n ) O\left(nlog^2\,n\right) O(nlog2n)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
const int INF=0x3f3f3f3f;
typedef unsigned long long uLL;
typedef pair<LL,LL> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,a[MAXN],g[MAXN][2],f[MAXN][2],head[MAXN],tot,idx,ld[MAXN];
int siz[MAXN],dfn[MAXN],id[MAXN],wson[MAXN],ltp[MAXN],father[MAXN];
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
struct matrix{
	int c[2][2];
	matrix(){memset(c,192,sizeof(c));}
	int* operator [] (const int &x){return c[x];}
	matrix operator * (const matrix &rhs)const{
		matrix res;
		for(int i=0;i<2;i++)
			for(int k=0;k<2;k++)
				for(int j=0;j<2;j++)
					res[i][j]=max(res[i][j],c[i][k]+rhs.c[k][j]);
		return res;
	}
	void print(){
		for(int i=0;i<2;i++,puts(""))
			for(int j=0;j<2;j++)
				printf("%d ",c[i][j]);
	}
}val[MAXN],tr[MAXN<<2],I;
void dosaka1(int u,int fa){
	siz[u]=1;wson[u]=0;father[u]=fa;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		dosaka1(v,u);siz[u]+=siz[v];
		if(siz[wson[u]]<siz[v])wson[u]=v;
	}
}
void dosaka2(int u,int tp){
	ltp[u]=tp;dfn[u]=++idx;g[u][1]=a[u];id[idx]=u;
	if(wson[u])dosaka2(wson[u],tp),ld[u]=ld[wson[u]];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==father[u]||v==wson[u])continue;
		dosaka2(v,v);g[u][1]+=f[v][0];g[u][0]+=max(f[v][0],f[v][1]);
	}
	if(wson[u])
		val[u][0][0]=val[u][0][1]=g[u][0],val[u][1][0]=g[u][1],
		f[u][0]=max(f[wson[u]][0],f[wson[u]][1])+g[u][0],f[u][1]=g[u][1]+f[wson[u]][0];
	else f[u][1]=a[u],ld[u]=u,val[u][0][0]=val[u][0][1]=0,val[u][1][0]=a[u];
}
void build(int rt,int l,int r){
	if(l>r)return ;int mid=l+r>>1;
	if(l==r){tr[rt]=val[id[l]];return ;}
	build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
	tr[rt]=tr[rt<<1]*tr[rt<<1|1];
}
void modify(int rt,int l,int r,int ai,matrix aw){
	if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;
	if(l==r){tr[rt]=aw;return ;}
	if(ai<=mid)modify(rt<<1,l,mid,ai,aw);
	if(ai>mid)modify(rt<<1|1,mid+1,r,ai,aw);
	tr[rt]=tr[rt<<1]*tr[rt<<1|1];
}
matrix query(int rt,int l,int r,int al,int ar){
	if(l>r||l>ar||r<al)return I;int mid=l+r>>1;
	if(al<=l&&r<=ar)return tr[rt];matrix res=I;
	if(ar<=mid)return query(rt<<1,l,mid,al,ar);
	if(al>mid)return query(rt<<1|1,mid+1,r,al,ar);
	return query(rt<<1,l,mid,al,ar)*query(rt<<1|1,mid+1,r,al,ar);
}
void ChainModify(int x,int y){
	g[x][1]-=a[x];a[x]=y;g[x][1]+=y;val[x][1][0]=g[x][1];
	modify(1,1,n,dfn[x],val[x]);x=ltp[x];
	while(x>1){
		matrix tmp=query(1,1,n,dfn[x],dfn[ld[x]]);int y=father[x];
		g[y][1]-=f[x][0];g[y][0]-=max(f[x][0],f[x][1]);
		f[x][0]=tmp[0][0];f[x][1]=tmp[1][0];
		g[y][1]+=f[x][0];g[y][0]+=max(f[x][0],f[x][1]);
		val[y][0][0]=val[y][0][1]=g[y][0];val[y][1][0]=g[y][1];
		modify(1,1,n,dfn[y],val[y]);x=ltp[y];
	}
}
signed main(){
	read(n);read(m);I[0][0]=I[1][1]=1;
	for(int i=1;i<=n;i++)read(a[i]);
	for(int i=1;i<n;i++){
		int u,v;read(u);read(v);
		addEdge(u,v);addEdge(v,u);
	}
	dosaka1(1,0);dosaka2(1,1);build(1,1,n);
	for(int i=1;i<=m;i++){
		int x,y;read(x);read(y);ChainModify(x,y);
		matrix tmp=query(1,1,n,dfn[1],dfn[ld[1]]);
		printf("%d\n",max(tmp[0][0],tmp[1][0]));
	}
	return 0;
}


谢谢!!!

updata 3.14-----------------------------------------------------------------------------
一早上起来发现这文章还能入选C/C++领域内容榜,没见过。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值