新的换根dp方法

前言

联考中考了换根dp,发现自己之前理解的换根好拉,特此记录一下高级的换根dp做法。

正文

比如有dp:

  • \(f_x\) 为包含以 \(x\) 为根的子树的dp值,\(son_x\) 表示 \(x\) 的子节点集合。

转移:

  • 先将 \(son_x\) 按dp值按从小到大排序,即 \(son_{x,1}\) 为子节点中dp值最大的。
  • 然后 \(f_x=\max_{i}\{f_{son_{x,i}}+i-1\}\)

在这种换根dp中,我们需要额外开一个数组 \(g_x\) 表示当根为 \(x\) 时,在以 \(1\) 为根的树中的\(fa_x\) 的dp值是多少。

这样,可以在根为 \(x\) 的时候扫一遍子节点得到,类似与这样:

void sous3(int x,int fa){
    扫一遍儿子得到ans[x]
	for(int i=h[x];i;i=a[i].ne){
        int y=a[i].y;
        if(y==fa)continue;
		处理 g[y];
	}
	for(int i=h[x];i;i=a[i].ne){
        int y=a[i].y;
        if(y==fa)continue;
		f[x]=g[y];
		sous3(y,x);
	}
}

因为处理g[x]可以用离线的做法(例如前后缀max处理去掉一个点的整体max),所以处理灵活,并且也少去了很多信息的记录和撤回。

在此题中,也可以用前后缀的方法,唯一不同的是需要让后缀答案\(-1\)

void sous3(int x,int fa){
	vector<int> b;
    //b用来存相邻的节点。
	for(int i=h[x];i;i=a[i].ne)b.push_back(a[i].y);
	sort(b.begin(),b.end(),paix2);
	for(int i=1;i<(int)b.size();++i)
		an[x]=max(an[x],f[b[i]]+i-1);
	an[x]=max(an[x],(int)b.size()-1)+f[b[0]];
    //扫一遍儿子得到ans[x]
	nema[b.size()]=0;
	for(int i=(int)b.size()-1;i>=0;--i)
		nema[i]=max(nema[i+1],f[b[i]]+i);
    //先处理后缀max,前缀max直接在求g的时候求
	int ma=0;
	for(int i=0;i<(int)b.size();++i)
		g[b[i]]=max(ma,nema[i+1]-1),ma=max(ma,f[b[i]]+i);
	for(int i=0;i<(int)b.size();++i){
		if(b[i]==fa)continue;
		int y=b[i];
		f[x]=max({g[y],1,(int)b.size()-1});
		sous3(y,x);
	}
}
posted @ 2022-02-23 17:13  qwq_123  阅读(117)  评论(0编辑  收藏  举报