介绍
基于树上dp的基本模型,题目初始不给出根节点,需要遍历每一个根节点,达到某一个根节点的最佳情况
给每一个点做一次树形DP,时间复杂度 O(n^2)
换根dp主要就是将时间复杂度降到O(n),在根结点切换时,直接通过一些已经计算过的数据在O(1)就能得到另一个根的结果
一般是通过二次扫描,第一次dfs获得预处理数据,第二次dfs进行根节点的转换
前置知识:树形DP
所以,换根DP的本质是树形DP,只不过是多了一个dfs进行根节点的转换。
原理
第一遍dfs
和树形DP一样,第一遍dfs都是用来初始化数据的,例如节点深度,最长路径等。
这一个过程是子→父的,基本可以套模板。
e.g.
void dfs(int u,int fa)
{
d[u]=d[fa]+1;//d[root]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa)
continue;
dfs(v,u);
}
}
第二遍dfs
第二遍dfs就很灵活了,它是基于我们的所求得出来的过程,而这个过程是父→子的
e.g.
如图,从1开始dfs,
到2,f[2]+=f[1]=3,
到5,f[5]+=f[2]=8,
到6,f[6]+=f[5]=14,
到4,f[4]+=f[2]=7…
根据不同需求,f可以是一维的,二维的,甚至是三维的
这一点一定要灵活变通
例题
换根DP例题
Code
#include<bits/stdc++.h>
#define mod 1
#define int long long
#define foru(i,a,b) for(int i=a;i<=b;i++)
#define ford(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+1;
int idx,head[N],n,m,d[N],f[N],x,y,z;
struct fy
{
int w,to,next;
}edge[N<<1];
char gc()
{
static char now[1<<20],*S,*T;
if(T==S)
{
T=(S=now)+fread(now,1,1<<20,stdin);
if(T==S)
return EOF;
}
return *S++;
}
template <typename T>
void Read(T&x)
{
x=0;
char c=gc();
while(c<'0'||c>'9')
c=gc();
x=c-'0';
while((c=gc())>='0'&&c<='9')
x=x*10+c-'0';
}
template <typename T, typename... Args>
void Read(T&x,Args&...args)
{
Read(x);
Read(args...);
}
inline void add(int u,int v,int w)
{
edge[idx].w=w,edge[idx].to=v,edge[idx].next=head[u];head[u]=idx++;
}
void dfs(int u,int fa)
{
d[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa)
continue;
dfs(v,u);
d[u]+=d[v];
}
}
void dfs_(int u,int fa)
{
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa)
continue;
f[v]=f[u]+n-2*d[v];
dfs_(v,u);
}
}
signed main()
{
memset(head,-1,sizeof(head));
Read(n);
foru(i,1,n-1)
Read(x,y),add(x,y,1),add(y,x,1);
dfs(1,0);
foru(i,1,n)
f[1]+=d[i];
dfs_(1,0);
int ans=0,sum=0;
foru(i,1,n)
if(f[i]>ans)
ans=f[i],sum=i;
printf("%lld",sum);
}