换根 DP 学习笔记

简介

换根 DP 是树形 DP 的一个分支,用于解决根节点不确定的问题,可以把 O ( n ) O(n) O(n) 枚举根节点的过程通过两次 dfs 优化掉,(据说)是一种重要的技巧。

分析

以一道经典例题 [POI2008]STA-Station 为例。

题意非常明了了,不解释了。

正如一般换根 DP 学习笔记的套路,我们先想想怎么 O ( n 2 ) O(n^2) O(n2) 的解决这个问题,非常简单,我们可以枚举一个根,然后 O ( n ) O(n) O(n) dfs 查询深度最后取 max ⁡ \max max

显然 O ( n 2 ) O(n^2) O(n2) 是通不过这个题的,那么应该怎样去优化呢?

一个不太好想到的想法是,如果知道了一个节点的答案,不难算出其子节点的答案。

这里直接给出换根 DP 做法,我们随便拉一个点出来,记为 u u u。现在我们钦定它就是根。

一次 dfs,算出答案,记为 f u f_u fu。同时我们做如下约定: w i w_i wi i i i 号点的重量(以 u u u 为整棵树的根中以 i i i 为根子树大小)。

考虑如果我们把根从 u u u 换成它的儿子节点 v v v ,会发生什么事。

左边是换根之前的示意图,右边是换根之后。

有图有真相,以 v v v 为根的子树深度整体减少了 1 1 1,其余的部分由于全是 u u u 的子树(新树中),所以深度随着 u u u 增加了 1 1 1

形式化的: f v = f u − w v + ( n − w v ) = f u + n − 2 w v f_v=f_u-w_v+(n-w_v)=f_u+n-2w_v fv=fuwv+(nwv)=fu+n2wv

于是乎,我们成功的把问题转化为了两次 dfs,这就是换根 DP。

最后放下例题代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
    
    
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){
    
    if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int head[1000005],depth[1000005],cnt,n,m,s,f[1000005],size[1000005],ans=0;
struct Edge{
    
    
	int u,v,next;
}e[2000005];
inline void add(int u,int v){
    
    
   e[++cnt].u=u;
   e[cnt].v=v;
   e[cnt].next=head[u];
   head[u]=cnt;
}
void dp1(int u,int fa){
    
    
	size[u]=1;
	for(int j=head[u];j;j=e[j].next){
    
    
		int v=e[j].v;
		if(v!=fa){
    
    
			depth[v]=depth[fa]+1;
			dp1(v,u);
			size[u]+=size[v];
		}
	}
}
void dp2(int u,int fa){
    
    
	for(int j=head[u];j;j=e[j].next){
    
    
		int v=e[j].v;
		if(v!=fa){
    
    
			f[v]=f[u]-2*size[v]+size[1];
			dp2(v,u);
		}
	}
}
signed main(){
    
    
	cin>>n;
   for(int i=1,u,v;i<=n-1;i++){
    
    
   cin>>u>>v;
   add(u,v);
   add(v,u); 
   }
   dp1(1,-1);
   for(int i=1;i<=n;i++){
    
    
   f[1]+=depth[i];
   }
   dp2(1,-1);
   int ans=0,id=114514; 
   for(int i=1;i<=n;i++){
    
    
   if(ans<f[i]){
    
    
   ans=f[i];
   id=i;
   }
   }
   cout<<id<<endl;
   return 0;
}

(码风不一样是吧,这是早期贺题作品)

例题

HDU5834 Magic boy Bi Luo with his excited tree

题意简述:给定一棵 n n n 个节点的树, 每个节点都有价值 a i a_i ai 每条边有花费 c i c_i ci,每经过一个点,就可以得到这个 a i a_i ai,但经过一条边会花费 c i c_i ci,并且价值只能得到一次但每次经过都要花费 c i c_i ci,求从每个节点出发可以得到最大利益。

保存了,明天写。

おすすめ

転載: blog.csdn.net/cryozwq/article/details/119219413