换根dp释义:
换根dp应该可以算作树形dp的一种。在一般的情况下我们进行树形dp的时候常常都会以一个固定的节点来作为根节点,但换根dp故名思意就是在进行树上dp操作时会需要进行换根操作的一类特殊的树形dp(个人理解)
进行换根的套路操作:
1、在树上自底向上进行dp操作
2、在树上自顶向下进行dp操作
3、根据题目要求计算答案
接下来以具体的题目作为例子进行换根dp的练习:
一、树的中心
题目链接
题意一目了然是要叫我们找到当前树中到其他节点最远距离最近的点的距离。
思路:
分析题意首先是要明确树中节点的最远距离只可能是当前节点到叶节点的距离。
所以我们只需要知道每个点到叶节点的最远距离再找出其中的最小距离即可。
再以下面这棵树为例:
当我们以1节点为根节点的时候,进行普通的树上dp时3节点便只会更新到5,6节点的距离,却并不能直接更新到4节点的距离,但我们可以通过1节点与3节点之间的关系得出3到4节点的距离(即将3节点看作根节点,将1节点看作子节点)。
我们定义d1[i]表示i节点正常通过子节点更新得到的最远距离,d2[i]表示i节点正常通过子节点更新得到的次远距离,up[i]表示i节点通过父节点更新得到的最远距离。
所以:
当前节点处于其父节点u的最远距离上时:
up[i]=max(up[u],d2[u])+对应边权
其他情况下:
up[i]=max(up[u],d1[u])+对应边权
相关代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+5,M=2e5+5;
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx]=c, h[a] = idx ++ ;
}
int d1[N],d2[N],up[N],hh[N];
void dfs_d(int u,int fa) {
d1[u]=d2[u]=0;
for(int i=h[u];~i;i=ne[i]) {
int tt=e[i];
if(tt==fa) continue;
dfs_d(tt,u);
int ff=d1[tt]+w[i];
if(ff>d1[u])
d2[u]=d1[u],d1[u]=ff,hh[u]=tt;
else if(ff>d2[u])
d2[u]=ff;
}
return ;
}
void dfs_u(int u,int fa) {
for(int i=h[u];~i;i=ne[i]) {
int tt=e[i];
if(tt==fa) continue;
if(tt==hh[u]) up[tt]=max(d2[u],up[u])+w[i];
else up[tt]=max(d1[u],up[u])+w[i];
dfs_u(tt,u);
}
return ;
}
int main()
{
int n; cin>>n;
memset(h, -1, sizeof h);
for(int i=1;i<n;i++){
int a,b,c; cin>>a>>b>>c;
add(a, b, c);
add(b, a, c);
}
dfs_d(1,-1);
dfs_u(1,-1);
int ans=0x3f3f3f3f;
for(int i=1;i<=n;i++) ans=min(ans,max(d1[i],up[i]));
cout<<ans<<endl;
return 0;
}
二、啊啊啊啊啊
题目链接
思路:
设d[i]表示i节点正常通过子节点更新能得到的深度值,up[i]表示i节点通过父节点u更新能得到的深度值,sz[i]表示节点i的子树大小,up_sz[i]表示通过父节点更新的节点个数。
d[i]=∑d[i的子节点]+sz[i]
up[i]=up[u]+d[u]-d[i]-sz[i]+up_sz[i];
相关代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define int long long
const int N=1e6+5,M=2e6+5;
int h[N],e[M],ne[M],idx;
int d[N],up[N],sz[N],up_sz[N];
void add(int a,int b) {
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
return ;
}
void dfs_d(int u,int fa) {
sz[u]=0;
for(int i=h[u];~i;i=ne[i]) {
int tt=e[i];
if(tt==fa) continue;
dfs_d(tt,u);
sz[u]+=sz[tt];
d[u]+=d[tt];
}
d[u]+=sz[u];
sz[u]+=1;
return ;
}
void dfs_u(int u,int fa) {
for(int i=h[u];~i;i=ne[i]) {
int tt=e[i];
if(tt==fa) continue;
int ll=d[u]-d[tt]-sz[tt];
up_sz[tt]=up_sz[u]+(sz[u]-sz[tt]);
up[tt]=up[u]+ll+up_sz[tt];
dfs_u(tt,u);
}
return ;
}
signed main() {
int n; cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++) {
int a,b; cin>>a>>b;
add(a,b);
add(b,a);
}
dfs_d(1,-1);
dfs_u(1,-1);
int mm=0x3f3f3f3f3f3f3f3f;
for(int i=1;i<=n;i++)
if(d[i]+up[i]<mm) mm=min(mm,d[i]+up[i]);
cout<<mm<<endl;
return 0;
}
三、哇哇哇哇哇
题目链接
题意:
根据给定的4个条件找到流量最大点的最大流量。
换根dp模板题:
相关代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int N=2e5+5,M=4e5+5;
int h[N],e[M],ne[M],w[M],idx;
void add(int a,int b,int c) {
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
return ;
}
int d[N],up[N];
int dd[N];
void dfs_d(int u,int fa) {
d[u]=0;
for(int i=h[u];~i;i=ne[i]) {
int tt=e[i];
if(tt==fa) continue;
if(dd[tt]==1) d[u]+=w[i];
else {
dfs_d(tt,u);
d[u]+=min(w[i],d[tt]);
}
}
return ;
}
void dfs_u(int u,int fa){
for(int i=h[u];~i;i=ne[i]) {
int tt=e[i];
if(tt==fa) continue;
int ll=up[u]+d[u]-min(w[i],d[tt]);
if(dd[u]==1) up[tt]=w[i];
else up[tt]=min(ll,w[i]);
dfs_u(tt,u);
}
return ;
}
int main() {
int t; cin>>t;
while(t--) {
int n; cin>>n;
for(int i=1;i<=n;i++) h[i]=-1,d[i]=0,up[i]=0,dd[i]=0;
idx=0;
for(int i=1;i<n;i++) {
int a,b,c; cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
dd[a]+=1;
dd[b]+=1;
}
dfs_d(1,-1);
dfs_u(1,-1);
int ans=-1;
for(int i=1;i<=n;i++) ans=max(ans,d[i]+up[i]);
cout<<ans<<endl;
}
return 0;
}
/*
5
1 2 1
2 3 1
3 4 1
4 5 1
*/