Codeforces Round #514 (Div. 2) E. Split the Tree (贪心 + 树上倍增)

题目链接:http://codeforces.com/contest/1059/problem/E

题目大意:给出一棵有 n 个结点的树,每个结点都有一个权值 w ,现在要你将这棵树分成若干条链,且每个结点只能属于一条链,分出来的链满足每条链上的结点不超过L个,同时这些结点的权值和不超过S。问你最少能把这棵树分成几条链。

题目思路:由于是要使得链尽可能的少,所以分出来的链每条链上的结点都是要尽可能的多的。

但如果从上往下去将树分链的话操作起来会很麻烦,所以我们可以考虑至下往上去分链。

由于叶子结点之间是不会有交集的,所以每个叶子结点必然会分出一条链来。然后我们就可以考虑至下往上贪心的做法,由于要使得链上结点尽可能的多,所以我们可以通过树上倍增的做法,预处理出每个结点从下往上形成的链最远能到哪里。

预处理出这个信息之后,我们就可以对答案进行求解了,每次就取不属于已取出链的一个结点,将其能形成的最长链变成一条新的链即可。

具体实现看代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
const int MX = 1e5 + 5;
const double eps = 1e-8;

int n, L, ans;
ll S, dis[MX];
vector<int>G[MX];
int val[MX], ST[MX][20], dep[MX], top[MX], head[MX];
//ST为倍增数组;dis数组表示从上往下的权值前缀和,这样求一条链的权值和只要减一下即可;
//dep为结点的深度;top表示该结点所能到达的最远结点;head表示分出来的链最上端的结点;

void ST_init() {
    for (int i = 1; i < 20; i++) {
        for (int j = 1; j <= n; j++)
            ST[j][i] = ST[ST[j][i - 1]][i - 1];
    }
}

void dfs1(int u, int fa) {
    dis[u] = dis[fa] + val[u];
    dep[u] = dep[fa] + 1;
    top[u] = u;
    int cnt = L;
    for (int i = 19; i >= 0; i--) {
        int f = ST[top[u]][i];
        if ((1 << i) >= cnt || f == 0) continue;
        if (dis[u] - dis[ST[f][0]] > S) continue;
        cnt -= (1 << i);
        top[u] = f;
    }
    for (auto v : G[u]) dfs1(v, u);
}

void dfs2(int u) {
    int res = -1;
    for (auto v : G[u]) {
        dfs2(v);
        if (head[v] == v) continue;
        if (res == -1 || dep[res] > dep[head[v]]) res = head[v];
    }
    if (res == -1) {
        res = top[u];
        ans++;
    }
    head[u] = res;
}

int main() {
    // FIN;
    scanf("%d%d%lld", &n, &L, &S);
    bool flag = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &val[i]);
        if (val[i] > S) flag = 1;
    }
    if (flag) {
        puts("-1");
        return 0;
    }
    for (int i = 2, p; i <= n; i++) {
        scanf("%d", &p);
        G[p].pb(i);
        ST[i][0] = p;
    }
    ST_init();
    dfs1(1, 0); dfs2(1);
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/83655360