美文网首页
Codeforces #514 Div2 E. Split th

Codeforces #514 Div2 E. Split th

作者: hfccccccccccccc | 来源:发表于2018-10-11 15:37 被阅读0次

    http://codeforces.com/problemset/problem/1059/E

    给一棵树,每个点有一些点权。定义垂直路径为:一条树上路径 v_1,v_2,\dots,v_m,对于 i \ge 2 都有 v_{i-1}v_i 的父亲。

    现在要把这棵树划分成若干条垂直路径,要求每条垂直路径的点个数不超过 L,点权和不超过 S,使每个节点都被一条垂直路径包含,问最少能用多少条路径划分。

    题解

    好像贪心就行了啊,计算每颗子树的最优解时,根节点可以是新开一条路径,或者使接上一条经过根节点的路径,并且这条路径向上延伸的长度是最长的。正确性。。。显然?

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    
    int rint() {
        int n, c, sgn = 0;
        while ((c = getchar()) < '-');
        if (c == '-') n = 0, sgn = 1;
        else n = c - '0';
        while ((c = getchar()) >= '0') n = 10 * n + c - '0';
        return sgn ? -n : n;
    }
    
    const int N = 100010;
    
    int n;
    int L;
    ll S;
    int par[N], gp[N][20];
    ll gw[N][20];
    int w[N];
    int up[N];
    int top[N];
    int dep[N];
    
    int main() {
        scanf("%d %d %lld", &n, &L, &S);
        for (int i = 0; i < n; i++) {
            scanf("%d", &w[i]);
            if (w[i] > S) {
                puts("-1");
                return 0;
            }
        }
        par[0] = -1;
        for (int i = 1; i < n; i++) {
            scanf("%d", &par[i]);
            par[i]--;
        }
    
        for (int i = 0; i < n; i++) {
            int p = par[i];
            gp[i][0] = p;
            if (p != -1) {
                gw[i][0] = w[p];
                dep[i] = dep[p] + 1;
            } else {
                gw[i][0] = 0;
                dep[i] = 0;
            }
            for (int j = 0; j < 19; j++) {
                if (gp[i][j] == -1) {
                    gp[i][j+1] = -1;
                } else {
                    gp[i][j+1] = gp[gp[i][j]][j];
                    gw[i][j+1] = gw[i][j] + gw[gp[i][j]][j];
                }
            }
    
            int A = 1;
            ll B = w[i];
            int x = i;
            for (int j = 19; j >= 0; j--) {
                if (gp[x][j] == -1) continue;
                if (A + (1 << j) <= L && B + gw[x][j] <= S) {
                    A += (1 << j);
                    B += gw[x][j];
                    x = gp[x][j];
                }
            }
            assert(x >= 0);
            up[i] = x;
        }
    
        int ans = 0;
        fill(top, top + n, -1);
        for (int i = n-1; i >= 0; i--) {
            if (top[i] == -1) {
                top[i] = up[i];
                ans++;
            }
            int p = par[i];
            if (dep[top[i]] <= dep[p] && (top[p] == -1 || dep[top[i]] < dep[top[p]])) {
                top[p] = top[i];
            }
        }
    
        printf("%d\n", ans);
    
        return 0;
    }
    

    总结

    这种用 p_i < i 来表示树的方法好评啊,不用递归了。

    相关文章

      网友评论

          本文标题:Codeforces #514 Div2 E. Split th

          本文链接:https://www.haomeiwen.com/subject/avmbaftx.html