CF 1039D You Are Given a Tree && CF1059E Split the Tree 的贪心解法

1039D 题意:

给你一棵树,要求对给定链长于 k = 1, 2, 3, ..., n,求出最大的链剖分。

1059E 题意:

给你一棵带权树,要求对于一组给定的 L, W 求出最小完全竖链剖分满足每条链点数不超过 L,权值和不超过 W。

显然两题是有共同点的,就是让我们求满足一定条件的树的最值链剖分。

比较暴力的可以尝试用 DP 计数,但是我不想深入 DP,因为方程比较复杂,思考起来不太容易。

很巧的是,这两题可以用相似的贪心思想来做。

在思考具体细节之前,需要明确贪心的主要思想:在从下往上回溯的过程中,总是在合适条件下贪心地成链。

1039D

如果对于给定的 k 值可以快速求解,就可以用分块的思想处理 k 不同时的情况。

怎样求解给定的 k 呢?

还是贪心的做法。

在 dfs 回溯过程中,一旦当前点可以成链,就直接钦定下来 :)

具体操作过程中,需要对每个点记录最长与次长子链,这样一旦两者和达到 k,就可以成链了。

证明略。

#include <bits/stdc++.h>

using namespace std;

const int N = 100000 + 5;
const int BLOCK = 100;

int n;
int f[N][2];
vector<int> node;
vector<int> g[N];
int fa[N];

void dfs(int u, int f = 1) {
  for (auto v: g[u]) {
    if (v != f) {
      dfs(v, u);
    }
  }
  node.push_back(u);
  fa[u] = f;
}

int solve(int k) {
  for (int i = 1; i <= n; i++) {
    f[i][0] = f[i][1] = 0;
  }
  int ret = 0;
  for (int u: node) {
    if (f[u][0] + f[u][1] + 1 >= k) {
      ret++;
    } else {
      if (f[fa[u]][0] < f[u][0] + 1) {
        f[fa[u]][1] = f[fa[u]][0];
        f[fa[u]][0] = f[u][0] + 1;
      } else {
        f[fa[u]][1] = max(f[fa[u]][1], f[u][0] + 1);
      }
    }
  }
  return ret;
}

int main() {
  scanf("%d", &n);
  for (int i = 1; i < n; i++) {
    int u, v;
    scanf("%d %d", &u, &v);
    g[u].push_back(v);
    g[v].push_back(u);
  }
  dfs(1);
  int x = N, k = 1;
  while (x > BLOCK) {
    x = solve(k++);
    printf("%d\n", x);
  }
  for (int i = BLOCK; i >= 0; i--) {
    if (solve(k) != i || k > n) {
      continue;
    }
    int l = k, r = n + 1;
    while (r - l > 1) {
      int mid = (l + r) / 2;
      if (solve(mid) == i) {
        l = mid;
      } else {
        r = mid;
      }
    }
    while (k <= l) {
      printf("%d\n", i);
      k++;
    }
  }
  return 0;
}

1059E

与上题不同的是,这题的链要求是竖直的,考虑从链底做贪心。

对于每个点,关注每条从子节点过来的链,并且贪心的选择将当前点并入终点最高的链上。

如果没有这样的链,就直接根据题目条件选择最高的链。

#include <bits/stdc++.h>

using namespace std;

const int N = 100000 + 5;

int up[N];
int dep[N], path[N];
int fa[N][21];
int n, L;
int w[N];
long long S;
long long sumw[N];
vector<int> g[N];

void prepare(int u, int f = 0) {
  dep[u] = dep[f] + 1;
  sumw[u] = sumw[f] + w[u];
  up[u] = u;
  fa[u][0] = f;
  for (int i = 1; i <= 20; i++) {
    fa[u][i] = fa[fa[u][i-1]][i-1];
  }
  int lim = L-1;
  for (int i = 20; i >= 0; i--) {
    if (fa[up[u]][i] != 0 && (1 << i) <= lim && sumw[u] - sumw[fa[fa[up[u]][i]][0]] <= S) {
      up[u] = fa[up[u]][i];
      lim -= (1 << i);
    }
  }
  for (int v: g[u]) {
    prepare(v, u);
  }
}

int solve(int u) {
  int ret = 0, best = -1;
  for (int v: g[u]) {
    ret += solve(v);
    if (path[v] != v) {
      if (best == -1 || dep[path[v]] < dep[best]) {
        best = path[v];
      }
    }
  }
  if (best == -1) {
    ret++;
    best = up[u];
  }
  path[u] = best;
  return ret;
}

int main() {
  scanf("%d %d %lld", &n, &L, &S);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &w[i]);
    if (w[i] > S) {
      printf("-1\n");
      return 0;
    }
  }
  for (int i = 2; i <= n; i++) {
    int p;
    scanf("%d", &p);
    g[p].push_back(i);
  }
  prepare(1);
  printf("%d\n", solve(1));
  return 0;
}

原文地址:https://www.cnblogs.com/HailJedi/p/9774769.html

时间: 2024-11-08 20:37:16

CF 1039D You Are Given a Tree && CF1059E Split the Tree 的贪心解法的相关文章

Solution: 题解 CF1059E Split the Tree

给出一个堆贪心解法 记点\(u\)的深度为\(d_u(d_1=0)\),父亲为\(f_u\),拥有儿子数量\(es_u\). 首先找到每个点的最远延伸点(点\(u\)的最远延伸点记为\(v_u\)),借助树上倍增即可. 接下来是贪心方法 在每次链连接完后删掉这些点,那么每条链的尾端一定是一个叶子. 那么就想办法找出目前贪心最优的叶子,然后往上连接. 以下的贪心本蒟蒻并没有想出严格证明方法,只能感性理解一下了QAQ. 贪心 1:目前\(d_{v_u}\)最小的叶子\(u\)是最优叶子 举个栗子:\

Binary Tree Postorder Traversal &amp;&amp; Binary Tree Preorder Traversal

详见:剑指 Offer 题目汇总索引:第6题 Binary Tree Postorder Traversal            Given a binary tree, return the postorder traversal of its nodes' values. For example: Given binary tree {1,#,2,3}, 1 2 / 3 return [3,2,1]. Note: Recursive solution is trivial, could y

leetcode - Binary Tree Preorder Traversal &amp;&amp; Binary Tree Inorder Traversal &amp;&amp; Binary Tree Postorder Traversal

简单来说,就是二叉树的前序.中序.后序遍历,包括了递归和非递归的方法 前序遍历(注释中的为递归版本): 1 #include <vector> 2 #include <stack> 3 #include <stddef.h> 4 #include <iostream> 5 6 using namespace std; 7 8 struct TreeNode 9 { 10 int val; 11 TreeNode *left; 12 TreeNode *rig

学习分享 lex tree diffrient row height(tree的行高调节)

Creating a Flex Tree with variable height rows can be mysteriously difficult if you are new to Flex item renderers.  This cookbook will give you the step by step instructions to creating a Tree control with variable height renderers. First you will n

[Leetcode][Tree][Depth of Binary Tree]

计算树的深度 1.minimum depth of binary tree 1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public:

LeetCode之“树”:Binary Tree Preorder Traversal &amp;&amp; Binary Tree Inorder Traversal &amp;&amp; Binary Tree Postorder Traversal

Binary Tree Preorder Traversal 题目链接 题目要求: Given a binary tree, return the preorder traversal of its nodes' values. For example: Given binary tree {1,#,2,3}, 1 2 / 3 return [1,2,3]. Note: Recursive solution is trivial, could you do it iteratively? 递归解

Split The Tree

Split The Tree 时间限制: 1 Sec  内存限制: 128 MB 题目描述 You are given a tree with n vertices, numbered from 1 to n. ith vertex has a value wiWe define the weight of a tree as the number of different vertex value in the tree.If we delete one edge in the tree, t

102. Binary Tree Level Order Traversal (Tree, Queue; BFS)

Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). For example:Given binary tree [3,9,20,null,null,15,7], 3   / \  9  20    /  \   15   7 return its level order traversal as: [  [3], 

POJ 题目3237 Tree(Link Cut Tree边权变相反数,求两点最大值)

Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 6131   Accepted: 1682 Description You are given a tree with N nodes. The tree's nodes are numbered 1 through N and its edges are numbered 1 through N ? 1. Each edge is associated with