luogu4719 动态dp

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/85267789

Description


给定一棵n个点的树,点带点权。

有m次操作,每次操作给定x,y,表示修改点x的权值为y。

你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

对于30%的数据,1≤n,m≤10
对于60%的数据,1≤n,m≤1000
对于100%的数据,1≤n,m≤105

Solution


传说中的动态dp

无修的版本非常好做,f[x,0/1]直接dp就可以了
对于有修改的版本我们类比dsu on tree,g[x,0/1]表示x除重儿子外选/不选x的答案,这样修改的时候一整条重链只需要改一次,并且叶节点的f就是g
同时,我们定义一种新的矩阵乘法 C = A B C i , j = max ( A i , k + B k , j ) C=A\cdot B\rightarrow C_{i,j}=\max(A_{i,k}+B_{k,j}) ,这种运算和矩阵乘法一样也是满足结合律的。那么我们就可以很方便地通过线段树矩阵乘法查询f,也就可以很方便地修改g

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

typedef long long LL;
const int INF=2147483647;
const int N=100005;

struct edge {int y,next;} e[N*2];
struct Matrix {
	LL rc[2][2];
	LL* operator [](int x) {return rc[x];}
	Matrix() {fill(rc,0);}
	Matrix operator *(Matrix B) {
		Matrix A=*this,C;
		C[0][0]=std:: max(A[0][0]+B[0][0],A[0][1]+B[1][0]);
		C[0][1]=std:: max(A[0][0]+B[0][1],A[0][1]+B[1][1]);
		C[1][0]=std:: max(A[1][0]+B[0][0],A[1][1]+B[1][0]);
		C[1][1]=std:: max(A[1][0]+B[0][1],A[1][1]+B[1][1]);
		/*rep(i,0,1) rep(j,0,1) {
			C[i][j]=-INF;
			rep(k,0,1) C[i][j]=std:: max(A[i][k]+B[k][j],C[i][j]);
		}*/
		return C;
	}
} rc[N<<2],f[N],res;

int pos[N],size[N],dep[N],fa[N],bl[N],ed[N];
int ls[N],edCnt;

LL w[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
	e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}

void dfs1(int x) {
	size[x]=1;
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y==fa[x]) continue;
		fa[e[i].y]=x; dep[e[i].y]=dep[x]+1;
		dfs1(e[i].y); size[x]+=size[e[i].y];
	}
}

void dfs2(int x,int up) {
	bl[x]=up; int mx=0;
	pos[x]=ed[x]=++pos[0];
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y!=fa[x]&&size[e[i].y]>size[mx]) mx=e[i].y;
	}
	if (!mx) return ;
	dfs2(mx,up); ed[x]=ed[mx];
	for (int i=ls[x];i;i=e[i].next) {
		if (e[i].y!=fa[x]&&e[i].y!=mx) dfs2(e[i].y,e[i].y);
	}
}

void query(int now,int tl,int tr,int l,int r) {
	if (r<l) return ;
	if (tl>=l&&tr<=r) return (void) (res=res*rc[now]);
	int mid=(tl+tr)>>1;
	query(now<<1,tl,mid,l,std:: min(r,mid));
	query(now<<1|1,mid+1,tr,std:: max(mid+1,l),r);
}

void modify(int now,int tl,int tr,int x) {
	if (tl==tr) return (void) (rc[now]=f[x]);
	int mid=(tl+tr)>>1;
	if (x<=mid) modify(now<<1,tl,mid,x);
	else modify(now<<1|1,mid+1,tr,x);
	rc[now]=rc[now<<1]*rc[now<<1|1];
}

Matrix ask(int x) {
	res[0][0]=res[1][1]=0;
	res[0][1]=res[1][0]=-INF;
	query(1,1,pos[0],pos[x],ed[x]);
	return res;
}

void change(int x,int v) {
	Matrix last,now;
	f[pos[x]][1][0]+=v;
	while (x) {
		last=ask(bl[x]);
		modify(1,1,pos[0],pos[x]);
		now=ask(bl[x]);
		x=fa[bl[x]];
		if (!x) break;
		f[pos[x]][0][0]+=std:: max(now[0][0],now[1][0])-std:: max(last[0][0],last[1][0]);
		f[pos[x]][0][1]=f[pos[x]][0][0];
		f[pos[x]][1][0]+=now[0][0]-last[0][0];
	}
}

int main(void) {
	freopen("data.in","r",stdin);
	int n=read(),m=read();
	rep(i,1,n) w[i]=read();
	rep(i,2,n) add_edge(read(),read());
	dfs1(dep[1]=1); dfs2(1,1);
	rep(i,1,n) f[i][1][1]=-INF;
	rep(i,1,n) change(i,w[i]);
	for (;m--;) {
		int x=read(),y=read();
		change(x,y-w[x]); w[x]=y;
		Matrix res=ask(1);
		printf("%lld\n", std:: max(res[0][0],res[1][0]));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/85267789