• 学习笔记——动态DP


    一直觉得DDP是一个神奇的东东,直到放弃了保卫王国的神奇倍增法之后才开始学习DDP

    模板题:

    给定一颗点带权的树,有(m)次修改,每次修改一个点的权值,要求在每次修改之后输出整棵树的最大权独立集的权值大小((n,mleq 10^5))


    暴力DP

    首先很容易得到没有修改操作时的dp方程(即没有上司的舞会

    (f_{i,1})表示选(i)(f_{i,0})表示不选(i)时的最大权独立集权值大小

    (f_{i,0}=Sigma{max(f_{v,1},f_{v,0})})

    (f_{i,1}=Sigma{f_{v,0}}+a_i)


    矩阵优化

    先将树进行重链剖分

    (g_{i,1})表示选择(i)(g_{i,0})表示不选(i)(i)不在重链上的儿子的(f)之和(即不算重儿子,(g)的转移同上)

    得到新的转移方程((son)为重儿子):

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

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

    这玩意可以变成矩阵形式

    (egin{bmatrix} f_{i,0}\ f_{i,1}\ end{bmatrix}=egin{bmatrix} g_{i,0} & g_{i,0}\ g_{i,1} & -infty\ end{bmatrix} ×)(egin{bmatrix} f_{son,0}\ f_{son,1}\ end{bmatrix})

    这里改变了一下矩阵的左乘运算,原来的
    (Sigma{a_{i,k}*b_{k,j}})
    变成了
    (max(a_{i,k}+b_{k,j}))

    由于(+)(max)同样满足(*)(+)的运算性质,所以这样替换后的矩阵乘法仍然满足之前的性质

    于是将每一个点i变成一个矩阵

    (egin{bmatrix} g_{i,0} & g_{i,0} \ g_{i,1} & -infty \ end{bmatrix})

    修改点权变成了修改矩阵,用一颗线段树来维护这些矩阵的乘积


    求解

    (ans=max(f_{i,0},f_{i,1})),重链剖分之后,(ans=query(1,end_{1})),end表示一条重链的终点(这条重链会从1号节点出发一直到某个叶子节点,而叶子节点没有儿子,在上面的式子中就相当于没有f矩阵,所以直接等于g矩阵),可以发现求(ans)的过程与(f)无关,所以我们成功的将(f)数组扔掉了,之后的修改操作只与矩阵有瓜

    修改一个点(I)的权值,会导致(g_{i,1})改变,但是由于与它在同一条重链上的父亲的(g)值与(i)无关((g)的定义说明了(g)值与重链上的点无关),我们不会修改这些点。由此还可以看出,我们只会修改两条重链交界的点,也就是每个(fa[top[i]]),由于重链只有(log)条,所以只会单点修改(log)次(这一段的思路和大部分树链剖分优化DP相同)

    整个算法的时间复杂度就是(O(2^3*nlog^2n))

    代码略长,因为是小萌新oier

    Code

    #include<bits/stdc++.h>
    #define N 100005
    #define Max(x,y) ((x)>(y)?(x):(y))
    #define Min(x,y) ((x)<(y)?(x):(y))
    using namespace std;
    typedef long long ll;
    const ll INF = 100000000000000;
    int n,m;
    int seg[N],rev[N],top[N],end[N],size[N],son[N],fa[N],dep[N],hfu;
    ll a[N],f[N][2];
    struct Matrix
    {
    	ll g[2][2];
    	Matrix () { memset(g,0,sizeof(g)); }
    	Matrix operator * (const Matrix &a)const
    	{
    		Matrix c;
    		for(int i=0;i<2;++i)
    			for(int j=0;j<2;++j)
    				for(int k=0;k<2;++k)
    				c.g[i][j]=Max(c.g[i][j],g[i][k]+a.g[k][j]);
    		return c;
    	}
    }t[N<<2],val[N];
    struct Edge
    {
    	int next,to;
    }edge[N<<1];int head[N],cnt=1;
    void add_edge(int from,int to)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	head[from]=cnt;
    }
    
    template <class T>
    void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
    }
    void dfs1(int rt)
    {
    	size[rt]=1;
    	dep[rt]=dep[fa[rt]]+1;
    	for(int i=head[rt];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa[rt]) continue;
    		fa[v]=rt;
    		dfs1(v);
    		size[rt]+=size[v];
    		if(size[v]>size[son[rt]]) son[rt]=v;
    		f[rt][0]+=Max(f[v][0],f[v][1]);
    		f[rt][1]+=f[v][0];
    	}
    }
    void dfs2(int rt)
    {
    	if(son[rt])
    	{
    		seg[son[rt]]=++hfu;
    		rev[hfu]=son[rt];
    		top[son[rt]]=top[rt];
    		end[top[rt]]=son[rt];
    		//还要搞一个重链的结尾位置 
    		dfs2(son[rt]);
    	}
    	for(int i=head[rt];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa[rt]||v==son[rt]) continue;
    		seg[v]=++hfu;
    		rev[hfu]=v;
    		top[v]=v;
    		end[v]=v;
    		dfs2(v);
    	}
    }
    
    void update(int rt,int l,int r,int x)
    {
    	if(l==r)
    	{
    		t[rt]=val[l];
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) update(rt<<1,l,mid,x);
    	else update(rt<<1|1,mid+1,r,x);
    	t[rt]=t[rt<<1]*t[rt<<1|1];
    }
    Matrix query(int rt,int l,int r,int x,int y)
    {
    	if(x<=l&&r<=y) return t[rt];
    	int mid=(l+r)>>1;
    	if(x<=mid&&y<=mid) return query(rt<<1,l,mid,x,y);
    	if(x>mid&&y>mid) return query(rt<<1|1,mid+1,r,x,y);
    	return query(rt<<1,l,mid,x,y)*query(rt<<1|1,mid+1,r,x,y);
    }
    void build(int rt,int l,int r)
    {
    	if(l==r)
    	{
    		int u=rev[l];
    		for(int i=head[u];i;i=edge[i].next)
    		{
    			int v=edge[i].to;
    			if(v==fa[u]||v==son[u]) continue;
    			t[rt].g[0][0]+=Max(f[v][0],f[v][1]);
    			t[rt].g[1][0]+=f[v][0];
    		}
    		t[rt].g[0][1]=t[rt].g[0][0];
    		t[rt].g[1][0]+=a[u];
    		val[l]=t[rt];
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(rt<<1,l,mid);
    	build(rt<<1|1,mid+1,r);
    	t[rt]=t[rt<<1]*t[rt<<1|1];
    }
    void modify_edge(int rt,ll w)
    {
    	val[seg[rt]].g[1][0]+=w-a[rt];
    	a[rt]=w;
    	Matrix las,now;
    	while(rt)
    	{
    		las=query(1,1,n,seg[top[rt]],seg[end[top[rt]]]);
    		update(1,1,n,seg[rt]);
    		now=query(1,1,n,seg[top[rt]],seg[end[top[rt]]]);
    		rt=fa[top[rt]];
    		val[seg[rt]].g[0][0]+=Max(now.g[0][0],now.g[1][0])-Max(las.g[0][0],las.g[1][0]);
    		val[seg[rt]].g[0][1]=val[seg[rt]].g[0][0];
    		val[seg[rt]].g[1][0]+=now.g[0][0]-las.g[0][0];
    	}
    }
    
    int main()
    {
    	read(n);read(m);
    	for(int i=1;i<=n;++i) read(a[i]),f[i][1]=a[i];
    	for(int i=1;i<n;++i)
    	{
    		int x,y;
    		read(x);read(y);
    		add_edge(x,y);
    		add_edge(y,x);
    	}
    	seg[1]=rev[1]=top[1]=hfu=1;
    	dfs1(1); dfs2(1);
    	build(1,1,n);
    	while(m--)
    	{
    		int x; ll y;
    		read(x);read(y);
    		modify_edge(x,y);
    		Matrix ans=query(1,1,n,seg[1],seg[end[1]]);
    		printf("%lld
    ",Max(ans.g[0][0] , ans.g[1][0]));
    	}
    	return 0;
    }
    

    cv一遍就可以将保卫王国过了,强制选或不选就等价于将某个点的点权赋为(-)INF,最小点权覆盖集=全集-最大点权独立集

  • 相关阅读:
    python全栈学习--day39(multiprocess模块)
    python全栈学习--day38(多进程在python中的操作)
    python全栈学习--day36(socket验证客户端的合法性、 并发编程)
    python全栈学习--day35(黏包机制)
    python全栈学习--day33(网络编程-socket)
    python-sys模块
    python-os模块
    python-生成随机数random
    python-常用模块-time、datetime模块
    python-模块、包
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11593851.html
Copyright © 2020-2023  润新知