换根 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)\) 直接过了。

posted @ 2024-04-09 21:49  lhc0707  阅读(8)  评论(0编辑  收藏  举报