zoukankan      html  css  js  c++  java
  • CF上部分树形DP练习题

    本次 5 道题均来自Codeforce

    关于树形DP的算法讲解:Here

    791D. Bear and Tree Jumps

    如果小熊每次能跳跃的距离为1,那么问题变为求树上任意两点之间距离之和。

    对于每一条边sum1和sum2分别表示边的左右点数,ans=各边的sum1*sum2之和即为答案。

    而本题最大跳跃距离为k,答案变为(ans+sum)/k。sum为每一条边需要多走x步才能整除k的x之和。
    树上任意两点间距离len=depth[x1]+depth[y1]-2*depth[f],f表示点x1和点y1的最近公共祖先。
    计算sum的方法:dp[i][j]表示到i点的距离对k取摸为j的点的总数。
    则对于任意两点a和b,dis需要满足(len[a][b]+dis)%k==0。
    每当搜索到一个点时,用O(k^2)的方法枚举所有情况。

    const int N = 2e5 + 10;
    vector<int>e[N];
    ll f[N][5] = {0}, a[N], ans;
    int n, k;
    void dfs(int u, int fa, int num) {
        f[u][num % k] = 1;
        a[u] = 1;
        for (int v : e[u]) {
            if (v == fa) continue;
            dfs(v, u, num + 1);
            for (int j = 0; j < k; ++j)
                for (int r = 0; r < k; ++r) {
                    int dis = (j + r - num * 2) % k;
                    int rev = (k - dis) % k;
                    ans += rev * f[u][j] * f[v][r];
                }
            a[u] += a[v];
            for (int j = 0; j < k; ++j) f[u][j] += f[v][j];
            ans += (n - a[v]) * a[v];
        }
    }
    int main() {
        cin.tie(nullptr)->sync_with_stdio(false);
        cin >> n >> k;
        for (int i = 1, x, y; i < n; ++i) {
            cin >> x >> y;
            e[x].push_back(y);
            e[y].push_back(x);
        }
        dfs(1, -1, 0);
        cout << ans / k << "
    ";
    }
    

    1120D. Power Tree

    实在没想到该如何去写出输出方案

    附一篇优秀题解:Here

    1153D. Serval and Rooted Tree

    似乎很板子?

    叶子节点dp[i]=1

    如果节点取max则dp[i]=min(dp[子节点们])

    如果取min则dp[i]+=dp[子节点们]

    答案就是 叶子节点个数+1-dp[1]

    int main() {
        cin.tie(nullptr)->sync_with_stdio(false);
        int n; cin >> n;
        vector<int> m(n);
        for (int &x : m) cin >> x;
        vector<int> sz(n);
        vector<vector<int>> e(n);
        for (int i = 1, x; i < n; ++i) {
            cin >> x;
            e[--x].push_back(i);
        }
        function<int(int)> dfs = [&](int u) {
            if (e[u].empty()) {
                sz[u] = 1;
                return 1;
            }
            int x = 0;
            if (m[u] == 1) x = n;
            sz[u] = 0;
            for (int v : e[u]) {
                int y = dfs(v);
                sz[u] += sz[v];
                if (m[u] == 1) x = min(x, sz[v] - y);
                else x += y - 1;
            }
            if (m[u] == 1) return sz[u] - x;
            else return x + 1;
        };
        cout << dfs(0) << "
    ";
    }
    

    735E. Ostap and Tree

    1060E. Sergey and Subway

    题意:给出一个树,把树上任意两个相隔一个点的点加一条边,问加完边之后任意两点的距离和是多少.

    先把问题转化一下,求树上所有点对的边距离和,那么针对每一条边他的贡献就是 一端点数*另一端点数 这是所有的要使用她的点对。 那么问题就被简化了,针对这道题目,可以得到距离为偶数的点 (x/2),距离为奇数的为 ((x+1)/2) ,那么就是奇偶层计算距离时需要加1,那么我们统计出这个再和之前每条边的贡献求和除2就是答案

    const int N = 240000;
    vector<int>e[N];
    ll  cnt, ans, n, f[N]; // f[N] 该点下方的点数
    void dfs(int u, int fa, int de) {
        f[u] = 1;
        cnt += de;
        for (int v : e[u]) {
            if (v == fa) continue;
            dfs(v, u, de ^ 1);
            f[u] += f[v];
        }
    }
    int main() {
        cin.tie(nullptr)->sync_with_stdio(false);
        cin >> n;
        for (int i = 1, x, y; i < n; ++i) {
            cin >> x >> y;
            e[x].push_back(y);
            e[y].push_back(x);
        }
        dfs(1, -1, 0);
        for (int i = 1; i <= n; ++i) ans += f[i] * (n - f[i]);
        ans += cnt * (n - cnt); //奇偶层的贡献
        cout << ans / 2;
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    ps中套索工具怎么使用的方法
    动态加载css方法实现和深入解析
    深入React事件系统(React点击空白部分隐藏弹出层;React阻止事件冒泡失效)
    近期项目中用到的一些自己写的或者整理而成的前端效果干货(********************************)
    Vue.js自定义指令的用法与实例
    vue初探
    React Router基础使用
    javascript常用积累
    js动画(三)
    基于jQuery的ajax系列之用FormData实现页面无刷新上传
  • 原文地址:https://www.cnblogs.com/RioTian/p/15133532.html
Copyright © 2011-2022 走看看