library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub sash2104/library

:heavy_check_mark: 木の直径 (ReRooting)
(test/aoj/GRL_5_A.rerooting.test.cpp)

Depends on

Code

// @brief 木の直径 (ReRooting)
#define PROBLEM "http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_5_A"

#include "../../graph/rerooting.hpp"
#include "../../monoid/max.hpp"

using namespace std;
#include <iostream>

// rerootingで、各頂点について最も遠い頂点との距離をもとめる。答えはその最大値
int main() {
  int N; cin >> N;
  auto f=[](int value, int cost) -> int { 
    if (value < 0) return cost;
    return value+cost;
  };
  ReRooting<int, monoid::max<int>> rerooting(N, f);
  for(int i = 1; i < N; i++) {
    int x, y, z; cin >> x >> y >> z;
    rerooting.add_edge(x, y, z);
  }
  if (N == 1) {
    cout << 0 << endl;
    return 0;
  }
  auto ret = rerooting.solve();
  int ans = 0;
  for (int i = 0; i < N; ++i) {
    ans = max(ans, ret[i]);
  }
  cout << ans << endl;
}
#line 1 "test/aoj/GRL_5_A.rerooting.test.cpp"
// @brief 木の直径 (ReRooting)
#define PROBLEM "http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_5_A"

#line 1 "graph/rerooting.hpp"
/**
 * @brief 全方位木DP (ReRooting)
 * https://qiita.com/keymoon/items/2a52f1b0fb7ef67fb89e
 * https://github.com/noshi91/blog/blob/master/codes/rerooting.cpp
 * 使用例: https://atcoder.jp/contests/typical90/submissions/24443488
 * 
 */
#include <cassert>
#include <functional>
#include <stack>
#include <vector>
#include <iostream>

#line 1 "graph/template.hpp"



#line 6 "graph/template.hpp"

template< typename T >
struct Edge {
  int from, to;
  T cost;
  Edge() {}
  Edge(int f, int t) : from(f), to(t), cost(1) {}
  Edge(int f, int t, T c) : from(f), to(t), cost(c) {}
  friend bool operator < (const Edge& lhs, const Edge& rhs) { return lhs.cost < rhs.cost; };
  friend bool operator > (const Edge& lhs, const Edge& rhs) { return rhs < lhs; };
  friend bool operator <= (const Edge& lhs, const Edge& rhs) { return !(lhs > rhs); };
  friend bool operator >= (const Edge& lhs, const Edge& rhs) { return !(lhs < rhs); };
};

template< typename T >
using Edges = std::vector< Edge< T > >;
template< typename T >
using Graph = std::vector< Edges< T > >;

template< typename T >
void debug(Graph<T> &g, int n = -1) {
    if (n == -1) n = g.size();
    for (int i = 0; i < n; ++i) {
        std::cerr << i  << "\t";
        for (auto &e: g[i]) {
            std::cerr << e.to << ",";
        }
        std::cerr << std::endl;
    }
}


#line 15 "graph/rerooting.hpp"

template <typename Cost, class Monoid>
struct ReRooting {
  struct Node {
    int to, rev;
    Cost cost;
  };
  typedef typename Monoid::value_t T;
  int n; // node数
  const Monoid monoid;

  std::vector<T> Res;
  std::vector<std::vector<T>> DP;
  std::vector<std::vector<Node>> g;

  using F = std::function<T(T, Cost)>;
  F f; //T f(T value, Cost cost) { } で定義される頂点の追加関数

  void add_edge(int u, int v, const Cost &c) {
    g[u].emplace_back((Node) {v, (int) g[v].size(), c});
    g[v].emplace_back((Node) {u, (int) g[u].size() - 1, c});
  }
  void add_edge(int u, int v, const Cost &c1, const Cost &c2) {
    g[u].emplace_back((Node) {v, (int) g[v].size(), c1});
    g[v].emplace_back((Node) {u, (int) g[u].size() - 1, c2});
  }

  ReRooting(int n, F f) : n(n), monoid(), g(n), f(f) {}
  std::vector<T> solve() {
    DP = std::vector<std::vector<T>>(n);
    Res = std::vector<T>(n, monoid.identity());
    if (n == 1) { // FIXME: n = 1も同様に扱える様にする
      std::cerr << "n=1は自分で求めること" << std::endl;
      assert(false);
    }

    for (int i = 0; i < n; i++) {
      DP[i] = std::vector<T>(g[i].size());
    }
    init();
    return Res;
  }

  void init() {
    // parents[i] := 一時的な根付き木として考えた時の、ノードiについての親
    std::vector<int> parents(n);
    std::vector<int> order(n); // dfsでの行きがけ順

    int index = 0;
    std::stack<int> stack;
    // 0を根とする
    stack.push(0);
    parents[0] = -1;
    // 行きがけ順を記録する
    while (stack.size() > 0) {
      int nodeId = stack.top();
      stack.pop();
      order[index++] = nodeId;
      for (auto &node: g[nodeId]) {
        if (node.to == parents[nodeId])
          continue;
        stack.push(node.to);
        parents[node.to] = nodeId;
      }
    }

    //帰りがけ順で部分木の値を求めていく
    for (int i = order.size() - 1; i >= 1; i--) {
      int nodeId = order[i];
      int parent = parents[nodeId];
      T accum = monoid.identity();
      int parentId = -1;
      for (int j = 0; j < g[nodeId].size(); j++) {
        if (g[nodeId][j].to == parent) {
          parentId = j;
          continue;
        }
        accum = monoid.merge(accum, DP[nodeId][j]);
      }
      int childId = g[nodeId][parentId].rev;
      DP[parent][childId] = f(accum, g[parent][childId].cost);
      // std::cerr << parent << "->" << childId << " " << DP[parent][childId] << std::endl;
    }

    //行きがけ順で頂点の値を確定させていく
    for (int i = 0; i < order.size(); i++) {
      int nodeId = order[i];
      T accum = monoid.identity();
      int numChild = g[nodeId].size();
      std::vector<T> rdp(numChild);
      rdp[numChild-1] = monoid.identity();
      for (int j = numChild-1; j >= 1; j--) {
        rdp[j - 1] = monoid.merge(DP[nodeId][j], rdp[j]);
      }
      for (int j = 0; j < numChild; j++) {
        auto &node = g[nodeId][j];
        DP[node.to][node.rev] = f(monoid.merge(accum, rdp[j]), g[node.to][node.rev].cost);
        accum = monoid.merge(accum, DP[nodeId][j]);
      }
      // FIXME: ここ、monoid.identity()の値を気をつけないとchild数が0の場合にバグる気がする
      Res[nodeId] = accum;
    }
  }
};
#line 1 "monoid/max.hpp"


#include <algorithm>
#include <limits>

namespace monoid {
template <class T>
struct max {
  typedef T value_t;
  T identity() const { return std::numeric_limits<T>::min();}
  T merge(T a, T b) const { return std::max(a, b); }
};
} // namespace monoid


#line 6 "test/aoj/GRL_5_A.rerooting.test.cpp"

using namespace std;
#line 9 "test/aoj/GRL_5_A.rerooting.test.cpp"

// rerootingで、各頂点について最も遠い頂点との距離をもとめる。答えはその最大値
int main() {
  int N; cin >> N;
  auto f=[](int value, int cost) -> int { 
    if (value < 0) return cost;
    return value+cost;
  };
  ReRooting<int, monoid::max<int>> rerooting(N, f);
  for(int i = 1; i < N; i++) {
    int x, y, z; cin >> x >> y >> z;
    rerooting.add_edge(x, y, z);
  }
  if (N == 1) {
    cout << 0 << endl;
    return 0;
  }
  auto ret = rerooting.solve();
  int ans = 0;
  for (int i = 0; i < N; ++i) {
    ans = max(ans, ret[i]);
  }
  cout << ans << endl;
}
Back to top page