ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题解 CF1551F Equidistant Vertices

2021-07-24 09:32:14  阅读:187  来源: 互联网

标签:std 那么 int 题解 dfs 枚举 Equidistant 距离 CF1551F


本想着 Div.3 玩玩就好,所以直接开了最后一题,想着 \(40\) 分钟内可以解决。然后就……悲剧了。

对借给我帐号的选手表示诚挚的歉意。

给定一棵 \(n\) 个节点的树,求选出 \(k\) 个点,它们两两距离相同的方案数。

首先容易发现一个性质,如果 \(k > 2\),那么这些点一定有一个“中心点”本身不被选中但所有选中的点到它距离都一样。

那么考虑枚举这一个中心点,以其为根把树变成有根树,考虑每一个距离求方案数。但这样还不行,因为如果有俩点的 LCA 不是根,那么它们之间的距离就和到其它点之间的距离不一样了。
所以还必须要 LCA 是根。那么就是要每个子树中最多只能选一个点。

现在想想方案怎么计数。
一开始想的是可能存在神奇的组合数方法,但想了一会儿未果,而且数据范围那么小,似乎并不是想让我们去推式子组合数的。所以考虑 dp。
\(f_{i, j}\) 表示根的前 \(i\) 个子树中一共有 \(j\) 个子树中取了点的方案数。那么转移就两种,当前子树取点或不取点,易得 \(f_{i, j} = f_{i-1,j} + f_{i-1, j-1} \times a_i\),其中 \(a_i\) 是第 \(i\) 棵子树的该层点数。

那么做法就很清晰了。

  1. 枚举一个根。
  2. dfs 记录每个子树下面每一个深度的点数。
  3. 枚举每一个深度。
  4. 进行 dp。

不要忘了好好地清空数组!我因为清空时写了:

for (int j = 1; j <= n; j++)
                for (int l = 0; l <= n; l++) num[i][j] = 0;

调了一个小时 qwq。

点击查看完整代码
#include <iostream>
#include <vector>
#define int long long
const int N = 205, P = 1000000007;
int T, n, k, x, y, num[N][N], f[N][N];
std::vector<int> g[N];
std::vector<int> a;
void dfs(int u, int fa) {
    num[u][0] = 1;
    for (int i = 0; i < (int)g[u].size(); i++) {
        int v = g[u][i];
        if (v == fa) continue;
        dfs(v, u);
        for (int j = 1; j <= n; j++)
            num[u][j] += num[v][j-1];
    }
}
signed main() {
    std::cin >> T;
    while (T--) {
        std::cin >> n >> k;
        int ans = 0;
        for (int i = 1; i < n; i++)
            std::cin >> x >> y, g[x].push_back(y), g[y].push_back(x);
        if (k == 2) {
            std::cout << n * (n-1) / 2 << '\n';
            for (int i = 1; i <= n; i++) g[i].clear();
            continue;
        }
        for (int i = 1; i <= n; i++) {
            dfs(i, 0);
            for (int j = 1; j <= n; j++) {
                for (int l = 0; l < (int)g[i].size(); l++) 
                    a.push_back(num[g[i][l]][j-1]);
                f[0][0] = 1;
                for (int x = 1; x <= (int)a.size(); x++) {
                    f[x][0] = 1;
                    for (int y = 1; y <= x; y++)
                        f[x][y] = (f[x-1][y] + f[x-1][y-1]*a[x-1]%P) % P;
                }
                ans = (ans + f[a.size()][k]) % P;
                for (int x = 1; x <= (int)a.size(); x++)
                    for (int y = 1; y <= x; y++) f[x][y] = 0;
                a.clear();
            }
            for (int j = 1; j <= n; j++)
                for (int l = 0; l <= n; l++) num[j][l] = 0;
        }
        std::cout << ans << '\n';
        for (int i = 1; i <= n; i++) g[i].clear();
        for (int i = 1; i <= n; i++)
            for (int j = 0; j <= n; j++) num[i][j] = 0;
    }
    return 0;
}

感觉这题充其量就 \(1900\),居然让我调了那么久,看样子以后 Div.3 还是有必要练习的。

标签:std,那么,int,题解,dfs,枚举,Equidistant,距离,CF1551F
来源: https://www.cnblogs.com/Acfboy/p/CF1551F.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有