换根 DP 学习笔记
换根 DP 处理的问题的特点是结果会随根的变化而变化,其的核心在两个字:调整。(或者说转化,但我更喜欢调整这个说法。)具体而言,就是先处理出以 1 为根节点时的答案,然后通过某种方式使得在计算子节点的答案时,根节点的答案可以为我们所用,也就是将根节点的答案调整调整,得到子节点的答案,一般的,这种“调整”的时间复杂度是 \(O(1)\) 的,这样我们能使总复杂度由暴力的 \(O(N^2)\) 降到 \(O(N)\)。
板子题:STA-Station
调整方式: \(f_u=f_{fa(u)}+n-siz_u-siz_u\)
代码:
#include<iostream>
#include<cstdio>
using namespace std;
#define N 1000005
#define int long long
int n,head[N],tot;
struct edge{
int v,nxt;
}e[N<<1];
void add(int u,int v)
{
e[++tot].v=v,e[tot].nxt=head[u],head[u]=tot;
}
int siz[N],dep[N],f[N];
void dfs(int u,int fath)
{
dep[u]=dep[fath]+1;
siz[u]=1;
f[1]+=dep[u];
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(v==fath)continue;
dfs(v,u);siz[u]+=siz[v];
}
}
int ans,tmp=0;
void dfs1(int u,int fath)
{
if(f[u]>tmp)tmp=f[u],ans=u;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(v==fath)continue;
f[v]=f[u]+(n-siz[v])-siz[v];
dfs1(v,u);
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
dfs(1,0);
dfs1(1,0);
cout<<ans<<"\n";
return 0;
}
AT_dp_v Subtree (link)
设 \(f_u\) 表示包含 \(u\) 的联通块在子树内的情况,\(g_u\) 表示包含 \(u\) 的连通块在子树外的情况。我们可以得到转移:
\[f_u=\prod_{v\in son\{u\}}(f_v+1)
\]
\[g_u=g_{fa}\times \prod_{v \in brother\{u\}}(f_v+1)+1
\]
这里我们不能直接除,因为我们取了模,于是我们选择记前后缀然后相乘。这样 \(O(N)\) 直接过了。