zoukankan      html  css  js  c++  java
  • 【洛谷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;
    }
    
  • 相关阅读:
    为什么电影里的黑客都不屑用鼠标? (转)
    专注做好一件事(转) focus---这个世界上最成功的人,他们在某一领域获得成功之后,可通过经营杠杆进入任何他们想要涉足的领域。而这都得依赖于他们曾极致的专注在做好一件事情上。
    世间万物都是遵循某种类似的规律,谁先把握了这些规律,谁就最早成为了强者。
    走的时候不要太急,有时间要停下来想一想当初为什么而走,这样,才会走的更稳,走的更明白。
    Android笔记: Android版本号
    Android笔记:真机调试无法输出Log 信息的问题
    阿里云服务器试用
    Android笔记:利用InputStream和BufferedReader 进行字节流 字符流处理
    Android笔记:java 中的数组
    Android笔记:C memory copy
  • 原文地址:https://www.cnblogs.com/stoorz/p/12303203.html
Copyright © 2011-2022 走看看