首页 > 其他分享 >FJOI 树的重心题解

FJOI 树的重心题解

时间:2023-08-05 11:22:20浏览次数:32  
标签:rt 子树 重心 int 题解 FJOI 节点 size

从零开始暴切省选题

题意简述

给定一个 \(n\) 个点的树,每个点的编号从 \(1\) 至 \(n\),问这个树有多少不同的连通子树,和这个树有相同的重心。

分析

1 求重心

首先要明确重心的定义。题目中给出:删掉某点 \(i\) 后,若剩余 \(k\) 个连通分量,那么定义 \(d(i)\) 为这些连通分量中点的个数的最大值,所谓重心,就是使得 \(d(i)\) 最小的点 \(i\)。这句话翻译成人话就是:给定一棵无根树,令任意 \(u\) 为根,使得最大子树最小的 \(u\) 即为该树的重心。由这个定义,我们可以明确重心的求法。先假设节点 \(1\) 为根,对于每一个节点 \(u\),求它的所有子树大小,并取其最大值,与已存储的根的最大子树比较,如果节点 \(u\) 的最大子树更小,那么更新根节点 \(rt\leftarrow u\)。这在搜索回溯时处理即可。特别注意的是,搜索时因为程序需要钦定 \(1\) 为根节点,因此在递归至其他节点时要注意:如果以 \(u\) 为根,则还有一棵大小为 \(n-size_u\) 的子树(\(n\) 为节点数),这也要计入子树中。

inline void getr(int u, int fa) {
    siz[u] = 1;
    dp[u] = 0;
    for(auto v : G[u]) {
        if(v != fa) {
            getr(v, u);
            siz[u] += siz[v];
            dp[u] = max(dp[u], siz[v]);
        }
    }
    dp[u] = max(dp[u], n - siz[u]);
    if(dp[rt] > dp[u] or rt == 0) {
        rt = u;
    }
}

2 分类

求出重心后注意到题目提示 基于以上定义,一个树的重心可能会有一个或者两个。这提示我们要进行分类讨论。那么分类的标准是什么呢?注意到上文提到的重心的定义中出现了最大子树最小这一 \(min-max\) 条件,稍加推导就可以得出重心的一条重要性质:点 \(u\) 是树的重心 \(\Leftrightarrow\) \(u\) 的所有子树大小不超过 \(\lfloor\dfrac{n}{2}\rfloor\)(证明···自己找,网上很多的)。

然后就可以发现,如果有两个重心,那个两个重心所在的子树大小相等,均为 \(\dfrac{n}{2}\),所以设已求得的重心为 \(rt\),如果满足 \(size_rt\times2==n\),则树有两个重心;否则树只有一个重心。

3 DP与转移

根据题中求联通子树个数这个要求及答案对 \(1e4+7\) 取模可以猜测,解法应为树上 \(DP\)。因此考虑状态。在已知两个子树的情况下,如果要合并答案可以知道,应该用乘法原理令两个子树选取的节点数相乘再求和,所以 \(f\) 状态要能够记录选取的节点数这一关键信息。于是发现可以定义状态 \(f_{u,i}\) 表示以 \(u\) 为根的子树,取 \(i\) 个节点的方案数。其中取 \(i\) 个节点可以解释为这棵联通子树里有 \(i\) 个节点。

考虑在 \(dfs\) 过程中求解 \(f_{u,i}\)(以下默认 \(u\) 为当前子树的根节点)。先求 \(size\) 数组,可知现在已合并到 \(u\) 的子树里的节点个数最多为 \(size_u\),设要合并的子树根节点为 \(v\),则节点最多有 \(size_v\) 个。由上文的分析不妨把子树 \(u\) 和子树 \(v\) 分开枚举,分别枚举 \(i\) 和 \(j\),则可以更新 \(f_{u,i+j}\) 的答案。注意到由于更新时要用到原 \(f\) 数组的数据,于是用一个 \(res\) 数组暂存结果,等到全部转移完再复制到 \(f_u\) 中。

由乘法原理可得对于 \(res_{i+j}\) 有如下式子:

\[res_{i+j}=\sum_{i=1}^{size_u}\sum_{j=0}^{size_v}f_{u,i}\times f_{v,j} \]

而初始化条件即为 \(f_{u,0}=f_{u,1}=1\)。

4 答案求解

由 \(2\) 中的分类,以重心数量为依据分开处理。

  • 如果重心有两个则较为简单。首先求重心时已求得一个重心,考虑到重心性质:如果有两个重心则两个重心相邻,可以直接枚举与 \(rt\) 相邻的节点 \(v\),如果 \(size_v=size_rt=\dfrac{n}{2}\),则 \(v\) 为另一个重心。定义 \(3\) 中的 \(dfs\) 为 \(dfs(u,fa)\), \(u\) 为当前节点,\(fa\) 为父节点,则直接 \(dfs(rt,v),dfs(v,rt)\) 就可以求出对于两个子树分别成立的 \(f\) 数组。然后在 \(1\sim n\) 枚举所有可能的树大小, \(ans\) 就可以快速地求出了:

\[ans=\sum_{i=1}^{n}f_{rt,i}\times f_{v,i} \]

inline void dfsdptwort(int u, int v) {
    memset(ddp, 0, sizeof ddp);
    dfs1(u, v);
    dfs1(v, u);
    for(int i = 1; i <= n; i++) {
        ans = (ans + ddp[u][i] * ddp[v][i]) % mod;
    }
}
  • 如果只有有一个重心,则枚举每个可能的树大小再重复一遍 \(dfs\) 中的求解过程就可以了。
inline void dfsdponlyrt(int u) {
    memset(ddp, 0, sizeof ddp);
    dfs1(u, 0);
    for(int i = 1; i <= n; i++) {
        memset(ddp[u], 0, sizeof ddp[u]);
        s[u] = 1;
        ddp[u][0] = ddp[u][1] = 1;
        for(auto v : G[u]) {
            for(int j = 1; j <= s[u] + s[v]; j++) {
                tmp[j] = 0;
            }
            for(int j = 1; j <= s[u]; j++) {
                for(int k = 0; k <= s[v]; k++) {
                    if(k * 2 >= i) {
                        break;
                    }
                    tmp[j + k] = (tmp[j + k] + ddp[u][j] * ddp[v][k] % mod) % mod;
                }
            }
            s[u] += s[v];
            for(int j = 1; j <= s[u]; j++) {
                ddp[u][j] = tmp[j];
            }
        }
        ans = (ans + ddp[u][i]) % mod;
    }
}

注意,由于有多组数据,每次数组和临时数据都要清空,尤其是用 \(vector\) 存储的邻接表用的 \(pushback\),尤其容易忘记。

总结

对于树上与子树或者数量有关问题,一般可以考虑树上 \(DP\),视情况可以在状态中存储子树大小,子树形态等信息,可以灵活运用计数原理,尤其在子树合并过程中

标签:rt,子树,重心,int,题解,FJOI,节点,size
From: https://www.cnblogs.com/George-Pig-Orz/p/17607673.html

相关文章

  • P4850 [IOI2009] Raisins 题解
    前言:IOI还出这样水的纯记忆化搜索题?还是T4?真令人难以置信。题意:题目传送门一个N×M的矩阵,对于任意一个子矩阵,只能横着或竖着分割,并且分割一次的价值为改子矩阵的元素之和,现要将该矩阵分割成1×1的方格,求最小的分割总价值之和。思路:看到这是个最优化的题,且数据范围很......
  • P9437 『XYGOI round1』一棵树 题解
    赛时一眼换根dp,然后调调调了大概1h+。题目传送门什么是换根dp在大多数树形dp中,我们只考虑对根的贡献,而一部分题目需要算出对所有点的贡献,一个比较显然的做法是对每个点都跑一次树形dp,但是大大增加了时间复杂度,是我们不能接受的。树形dp中的换根dp问题又被称为二次扫......
  • 洛谷 P7911 [CSP-J 2021] 网络连接 题解
    写在前面一道普及级别的题目。CSP-J全国统一命题2021年第三题。本题解来自于一位真正的大佬。传送门https://www.luogu.com.cn/blog/xyf007/solution-p7911。题面信息来源于洛谷。请访问https://www.luogu.com.cn/problem/P7911。声明:本题解非商业用途,一切侵权行为请联系作......
  • We Were Both Childrent 题解
    将一个好理解的方法。题目说有n只青蛙,每只青蛙初始都在0位置,每秒会往前跳a_i。你可以在位置1到n设置一个陷阱,陷阱会抓住经过它的所有青蛙,求你最多能抓住多少青蛙。很简单,只要枚举质因数并判断是否合法即可。intn,ans=0;cin>>n;memset(cnt,0,sizeofcnt)......
  • Balanced Round 题解
    原题链接。题目大意给你一些数,问至少删掉多少数后两两不大于k。我们可以画图理解。最后我们取max(2,1),由于我们求的是合法的,所以还得用长度减去2,最终答案就是2。根据图我们就知道可以遍历一遍数组,用t记录下最长合法序列长度,最后用n-t即可。代码#include<bits/......
  • CF1491B Minimal Cost 题解
    调了两个多小时终于过了,交一发题解。题目分析如果你认真读题就会发现,这道题看似有很多种情况,但障碍的移动方式其实只有几种。如果当所有障碍物都在一列时,可以将某一个障碍水平移动一格,再垂直移动一格或者水平移动两格,那么答案就是v+min(u,v)。当有通路时,则无需移动,答案就是......
  • CF1682B AND Sorting 题解
    首先,我们按照题意,可以用0来作为中间的一个数来交换其他两个数,这种元素肯定是有的,那就是所有不在正确位置上的所有数的AND值,我们可以开一个数组a来模拟这个过程,a_i&a_j=X,那这里的X就起到我们的0的作用了。代码:#include<bits/stdc++.h>#defineintlonglongusin......
  • [NOI2021] 路径交点 题解
    [NOI2021]路径交点题解题意给定一张\(k\)层的有向图,第\(i\)层有\(n_i\)​个顶点,第​\(1\)层与第\(k\)​层顶点数相同。对于第​​\(j\)\((1\leqj<k)\)层的顶点,只会连向第\(j+1\)层的顶点。没有边连向第\(1\)层的顶点,第\(k\)层的顶点不会向其他顶点连边......
  • [Luogu P8744] 左孩子右兄弟 题解
    题目大意给定一棵节点个数为\(N\)的多叉树,求其通过"左孩子右兄弟"表示法转化成的二叉树,高度最高是多少。解决思路首先分辨出此题目是树状DP,并了解"左孩子右兄弟"表示法的转换方式,便开始考虑DP的状态转移方程。状态由于每个节点由\(1\)至\(N\)编号,那么就使用\(dp_{k......
  • 【题解】[2023牛客多校] Distance
    题目传送门:[2023牛客多校]Distance题意对于任意两个元素个数相同的set:A、B,每次可以执行以下两种操作之一:将A中的任意元素加一将B中的任意元素加一\(C(A,B)\)含义为将\(A、B\)改变为完全相同的set所需要花费的最小次数;初始给你两个set:\(S、T\),计算\(\sum_{A\subs......