【洛谷P4719】【模板】动态 DP

题目

题目链接:https://www.luogu.com.cn/problem/P4719
给定一棵\(n\)个点的树,点带点权。
\(m\)次操作,每次操作给定\(x,y\),表示修改点\(x\)的权值为\(y\)
你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

思路

调到心态爆炸。。。从前天晚上开始就刚这道题。。。
最大权独立子集即“选出若干个不相邻的点使得他们的权值最大”。——摘自AKIOI的神仙
若没有修改,这道题就是树形\(dp\)入门题,设\(f[x][0/1]\)表示以\(x\)为根的子树,节点\(i\)选不选的最大权独立子集。那么有转移

\[f[u][0]=\sum_{v\in u's\ son}max(f[v][0],f[v][1]) \]

\[f[u][1]=\sum_{v\in u's\ son}f[v][0] \]

\(ddp\)是维护序列问题的,我们考虑将这棵树剖一下,变成若干条重链计算。
那么既然我们重链用矩阵乘法维护,那么轻链就要先计算出来。设\(g[x][0/1]\)表示不看\(x\)的重儿子的最大权独立子集,转移显然。
这样我们就可以改写\(f\)的转移

\[f[u][0]=g[u][0]+max(f[v][0],f[v][1]) \]

\[f[u][1]=g[u][1]+f[v][0] \]

其中\(v\)\(u\)的重儿子。
写成矩阵,有

\[\begin{bmatrix}f[v][0] \\ f[v][1] \end{bmatrix} \begin{bmatrix} g[u][0] &g[u][0] \\ g[u][1] &-\infty \end{bmatrix} = \begin{bmatrix} f[u][0]\\ f[u][1] \end{bmatrix}\]

假设我们修改点\(x\),这样我们就可以修改\(g[top[x]]\),然后再利用\(g[top[x]]\)修改\(g[fa[top[x]]]\),再利用重链往上修改。
细节算是比较多吧,调的很恶心。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=100010,Inf=107374323;
int head[N],a[N],f[N][2],g[N][2],son[N],rk[N],id[N],top[N],size[N],fa[N],end[N];
int n,Q,tot;

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void dfs1(int x,int father)
{
	f[x][0]=0; f[x][1]=a[x];
	size[x]=1; fa[x]=father;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=father)
		{
			dfs1(v,x);
			size[x]+=size[v];
			f[x][0]+=max(f[v][0],f[v][1]);
			f[x][1]+=f[v][0];
			if (size[v]>size[son[x]]) son[x]=v;
		}
	}
}

void dfs2(int x,int tp)
{
	g[x][0]=0; g[x][1]=a[x];
	id[x]=++tot; rk[tot]=x; top[x]=tp; end[tp]=tot;
	if (son[x]) dfs2(son[x],tp);
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa[x] && v!=son[x])
		{
			dfs2(v,v);
			g[x][0]+=max(f[v][0],f[v][1]);
			g[x][1]+=f[v][0];
		}
	}
}

struct matrix
{
	int a[3][3];
	
	matrix()
	{
		a[1][1]=a[1][2]=a[2][1]=a[2][2]=-Inf;
	}
	
	friend matrix operator *(matrix a,matrix b)
	{
		matrix c;
		for (int i=1;i<=2;i++)
			for (int j=1;j<=2;j++)
				for (int k=1;k<=2;k++)
					c.a[i][j]=max(c.a[i][j],a.a[i][k]+b.a[k][j]);
		return c;
	}
}M[N];

struct SegTree
{
	int l[N*4],r[N*4];
	matrix f[N*4];
	
	void pushup(int x)
	{
		f[x]=f[x*2]*f[x*2+1];
	}
	
	void build(int x,int ql,int qr)
	{
		l[x]=ql; r[x]=qr;
		if (ql==qr)
		{
			f[x]=M[rk[ql]];
			return;
		}
		int mid=(l[x]+r[x])>>1;
		build(x*2,ql,mid); build(x*2+1,mid+1,qr);
		pushup(x);
	}
	
	matrix ask(int x,int ql,int qr)
	{
		if (l[x]==ql && r[x]==qr) return f[x];
		int mid=(l[x]+r[x])>>1;
		if (qr<=mid) return ask(x*2,ql,qr);
		if (ql>mid) return ask(x*2+1,ql,qr);
		return ask(x*2,ql,mid)*ask(x*2+1,mid+1,qr);
	}
	
	void update(int x,int k)
	{
		if (l[x]==k && r[x]==k)
		{
			f[x]=M[rk[k]];
			return;
		}
		int mid=(l[x]+r[x])>>1;
		if (k<=mid) update(x*2,k);
			else update(x*2+1,k);
		pushup(x);
	}
}seg;

void Update(int x,int val)
{
	M[x].a[2][1]=M[x].a[2][1]-a[x]+val;
	a[x]=val;
	while (x)
	{
		matrix last=seg.ask(1,id[top[x]],end[top[x]]);
		seg.update(1,id[x]);
		matrix now=seg.ask(1,id[top[x]],end[top[x]]);
		x=fa[top[x]];
		M[x].a[1][1]=M[x].a[1][2]=M[x].a[1][1]-max(last.a[1][1],last.a[2][1])+max(now.a[1][1],now.a[2][1]);
		M[x].a[2][1]=M[x].a[2][1]-last.a[1][1]+now.a[1][1];
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&Q);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	tot=0;
	dfs1(1,0); dfs2(1,1);
	for (int i=1;i<=n;i++)
	{
		M[i].a[1][1]=M[i].a[1][2]=g[i][0];
		M[i].a[2][1]=g[i][1];
		M[i].a[2][2]=-Inf;
	}
	seg.build(1,1,n);
	while (Q--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		Update(x,y);
		matrix ans=seg.ask(1,id[1],end[1]);
		printf("%d\n",max(ans.a[1][1],ans.a[2][1]));
	}
	return 0;
}
posted @ 2020-02-13 12:54  stoorz  阅读(145)  评论(0编辑  收藏  举报