[复习]动态dp

[复习]动态dp

你还是可以认为我原来写的动态dp就是在扯蛋。

[Luogu4719]【模板】动态dp

首先作为一个\(dp\)题,我们显然可以每次修改之后都进行暴力\(dp\),设\(f[i][0/1]\)表示当前考虑\(i\)及其子树内的点,当前这个点是选还是不选时能够得到的最大权值,那么我们可以得到转移:\(f[i][0]+=\max\{f[v][0],f[v][1]\},f[i][1]+=f[v][0]\),其中\(v\)\(i\)的一个儿子。
那么这样子的复杂度就是\(O(qn)\)
仔细想想我们这样子为什么慢?因为我们重复计算了大量相同的转移,那么我们发现我们可以每次只需要修改当前的修改点到达根节点的这条链的所有点的\(dp\)值就好了,然而这样子复杂度最差还是\(O(nq)\)的。
那么我们还有哪里慢呢?
仔细想想,我们的转移都是一模一样的,区别只是在于每次的取值不同而已。
先考虑一模一样的转移可以怎么处理,似乎可以矩阵乘法?那么转移可以写成:

\[\begin{bmatrix}0&0\\V_u&-\infty\end{bmatrix}\times \begin{bmatrix}f_{v,0}\\f_{v,1}\end{bmatrix}=\begin{bmatrix}f_{u,0}\\f_{u,1}\end{bmatrix} \]

然而这样子的转移只有在这个点转移第一个儿子的时候是对的。
如果有多个儿子的话,我们把\(V_u\)看成除了这个儿子之外的\(f[u][1]\),\(0\)看成除了这个儿子之外的\(f[u][0]\)
那么等价于是我们要把儿子给分成两个部分,第一部分表示的是这个特殊转移的儿子,第二部分是剩下的儿子。似乎可以重链剖分?
那么对于重儿子我们可以直接维护这个矩阵,这样子用线段树维护矩阵乘法就可以算出重儿子部分的贡献,其他轻儿子暴力往上贡献。
看起来这个东西复杂度就很对了啊,单次修改只会造成\(log\)次暴力修改。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
const ll inf=1e17;
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,Q,V[MAX];
int fa[MAX],top[MAX],bot[MAX],size[MAX],hson[MAX],dfn[MAX],tim,ln[MAX];
void dfs1(int u,int ff)
{
	fa[u]=ff;size[u]=1;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		dfs1(v,u);size[u]+=size[v];
		if(size[v]>size[hson[u]])hson[u]=v;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;dfn[u]=++tim,ln[tim]=u;
	if(hson[u])dfs2(hson[u],tp),bot[u]=bot[hson[u]];
	else bot[u]=u;
	for(int i=h[u];i;i=e[i].next)
		if(e[i].v!=fa[u]&&e[i].v!=hson[u])
			dfs2(e[i].v,e[i].v);
}
ll f[MAX][2];
void dfs(int u,int ff)
{
	f[u][0]=0;f[u][1]=V[u];
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		dfs(v,u);
		f[u][0]+=max(f[v][0],f[v][1]);
		f[u][1]+=f[v][0];
	}
}
struct Matrix
{
	ll a[2][2];
	ll*operator[](int x){return a[x];}
}t[MAX<<2],tmp[MAX];
Matrix operator*(Matrix a,Matrix b)
{
	Matrix c;
	c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]);
	c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]);
	c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]);
	c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);
	return c;
}
void Build(int now,int l,int r)
{
	if(l==r)
	{
		int u=ln[l];ll g0=0,g1=V[u];
		for(int i=h[u];i;i=e[i].next)
			if(e[i].v!=fa[u]&&e[i].v!=hson[u])
				g0+=max(f[e[i].v][0],f[e[i].v][1]),g1+=f[e[i].v][0];
		tmp[l]=t[now]=(Matrix){g0,g0,g1,-inf};
		return;
	}
	int mid=(l+r)>>1;
	Build(lson,l,mid);Build(rson,mid+1,r);
	t[now]=t[lson]*t[rson];
}
void Modify(int now,int l,int r,int p)
{
	if(l==r){t[now]=tmp[l];return;}
	int mid=(l+r)>>1;
	if(p<=mid)Modify(lson,l,mid,p);
	else Modify(rson,mid+1,r,p);
	t[now]=t[lson]*t[rson];
}
Matrix Query(int now,int l,int r,int L,int R)
{
	if(L==l&&r==R)return t[now];
	int mid=(l+r)>>1;
	if(R<=mid)return Query(lson,l,mid,L,R);
	if(L>mid)return Query(rson,mid+1,r,L,R);
	return Query(lson,l,mid,L,mid)*Query(rson,mid+1,r,mid+1,R);
}
void Modify(int u,int w)
{
	tmp[dfn[u]][1][0]+=w-V[u];V[u]=w;
	while(u)
	{
		Matrix a=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
		Modify(1,1,n,dfn[u]);
		Matrix b=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
		u=fa[top[u]];if(!u)break;int x=dfn[u];
		ll g0=a[0][0],g1=a[1][0],f0=b[0][0],f1=b[1][0];
		tmp[x][0][0]=tmp[x][0][1]=tmp[x][0][0]+max(f0,f1)-max(g0,g1);
		tmp[x][1][0]=tmp[x][1][0]+f0-g0;
	}
}
int main()
{
	n=read();Q=read();
	for(int i=1;i<=n;++i)V[i]=read();
	for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
	dfs1(1,0);dfs2(1,1);dfs(1,0);Build(1,1,n);
	while(Q--)
	{
		int x=read(),w=read();Modify(x,w);
		Matrix ans=Query(1,1,n,dfn[1],dfn[bot[1]]);
		printf("%lld\n",max(ans[0][0],ans[1][0]));
	}
	return 0;
}

[NOIP2018]保卫王国

要求最小点覆盖。这个东西等于权值和减去最大独立集。
而最大独立集就是上面的那个东西。
考虑如何强制某个点必须选或者不选,如果必须选则将其权值减去一个\(inf\),如果必须不选则将其权值加上一个\(inf\)
然后就做完了。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
const ll inf=1e17;
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,Q;ll V[MAX];
int fa[MAX],top[MAX],bot[MAX],size[MAX],hson[MAX],dfn[MAX],tim,ln[MAX];
void dfs1(int u,int ff)
{
	fa[u]=ff;size[u]=1;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		dfs1(v,u);size[u]+=size[v];
		if(size[v]>size[hson[u]])hson[u]=v;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;dfn[u]=++tim,ln[tim]=u;
	if(hson[u])dfs2(hson[u],tp),bot[u]=bot[hson[u]];
	else bot[u]=u;
	for(int i=h[u];i;i=e[i].next)
		if(e[i].v!=fa[u]&&e[i].v!=hson[u])
			dfs2(e[i].v,e[i].v);
}
ll f[MAX][2];
void dfs(int u,int ff)
{
	f[u][0]=0;f[u][1]=V[u];
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(v==ff)continue;
		dfs(v,u);
		f[u][0]+=max(f[v][0],f[v][1]);
		f[u][1]+=f[v][0];
	}
}
struct Matrix
{
	ll a[2][2];
	ll*operator[](int x){return a[x];}
}t[MAX<<2],tmp[MAX];
Matrix operator*(Matrix a,Matrix b)
{
	Matrix c;
	c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0]);
	c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1]);
	c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0]);
	c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1]);
	return c;
}
void Build(int now,int l,int r)
{
	if(l==r)
	{
		int u=ln[l];ll g0=0,g1=V[u];
		for(int i=h[u];i;i=e[i].next)
			if(e[i].v!=fa[u]&&e[i].v!=hson[u])
				g0+=max(f[e[i].v][0],f[e[i].v][1]),g1+=f[e[i].v][0];
		tmp[l]=t[now]=(Matrix){g0,g0,g1,-inf};
		return;
	}
	int mid=(l+r)>>1;
	Build(lson,l,mid);Build(rson,mid+1,r);
	t[now]=t[lson]*t[rson];
}
void Modify(int now,int l,int r,int p)
{
	if(l==r){t[now]=tmp[l];return;}
	int mid=(l+r)>>1;
	if(p<=mid)Modify(lson,l,mid,p);
	else Modify(rson,mid+1,r,p);
	t[now]=t[lson]*t[rson];
}
Matrix Query(int now,int l,int r,int L,int R)
{
	if(L==l&&r==R)return t[now];
	int mid=(l+r)>>1;
	if(R<=mid)return Query(lson,l,mid,L,R);
	if(L>mid)return Query(rson,mid+1,r,L,R);
	return Query(lson,l,mid,L,mid)*Query(rson,mid+1,r,mid+1,R);
}
void Modify(int u,ll w)
{
	tmp[dfn[u]][1][0]+=w-V[u];V[u]=w;
	while(u)
	{
		Matrix a=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
		Modify(1,1,n,dfn[u]);
		Matrix b=Query(1,1,n,dfn[top[u]],dfn[bot[u]]);
		u=fa[top[u]];if(!u)break;int x=dfn[u];
		ll g0=a[0][0],g1=a[1][0],f0=b[0][0],f1=b[1][0];
		tmp[x][0][0]=tmp[x][0][1]=tmp[x][0][0]+max(f0,f1)-max(g0,g1);
		tmp[x][1][0]=tmp[x][1][0]+f0-g0;
	}
}
int main()
{
	n=read();Q=read();scanf("%*s");ll sum=0;
	for(int i=1;i<=n;++i)sum+=(V[i]=read());
	for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
	dfs1(1,0);dfs2(1,1);dfs(1,0);Build(1,1,n);
	while(Q--)
	{
		int a=read(),x=read(),b=read(),y=read(),pa=V[a],pb=V[b];
		ll pls=0;
		if(!x)Modify(a,pa+inf),pls+=inf;else Modify(a,pa-inf);
		if(!y)Modify(b,pb+inf),pls+=inf;else Modify(b,pb-inf);
		Matrix ret=Query(1,1,n,dfn[1],dfn[bot[1]]);
		ll ans=max(ret[0][0],ret[1][0])-pls;
		if(ans<0)puts("-1");
		else printf("%lld\n",sum-ans);
		Modify(a,pa);Modify(b,pb);
	}
	return 0;
}

[SDOI2017]切树游戏

戳这里

posted @ 2019-03-21 13:31  小蒟蒻yyb  阅读(748)  评论(1编辑  收藏  举报