Problem p03450 (C++)

Buggy submission (status: Runtime Error):
// g++ -std=c++14 test.cpp -o test.out
// 問題URL
// https://atcoder.jp/contests/abc087/tasks/arc090_b

// 重み付きUnion find
// 参考：https://qiita.com/drken/items/cce6fc5c579051e64fab

#include <algorithm>
#include <bitset>
#include <cassert>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits.h>
#include <map>
#include <math.h>
#include <queue>
#include <set>
#include <stdlib.h>
#include <string.h>
#include <utility>
#include <vector>
using namespace std;

#define LL long long int
const LL INF = (LL)(1e18) + 1;
const int INF_INT = 2147483647 - 1e6 - 1;
const LL mod = 1000000007ll;
const int mod_int = 1000000007;

struct UnionFind {
  vector<LL> parent;      // 親ノード
  vector<LL> rank;        // ランク
  vector<LL> diff_weight; // 親ノードとの重みの差分

  UnionFind(LL n = 1) { init(n); }

  void init(LL n = 1) {
    parent.resize(n);
    rank.resize(n);
    diff_weight.resize(n);
    for (LL i = 0; i < n; ++i)
      parent[i] = i, rank[i] = 0, diff_weight[i] = 0;
  }

  // 重みつき Union-Find では、経路圧縮のついでに差分重み diff_weight も更新する
  LL root(LL x) {
    if (parent[x] == x)
      return x;
    else {
      LL r = root(parent[x]);
      diff_weight[x] += diff_weight[parent[x]]; // 累積和をとる
      return parent[x] = r;
    }
  }

  // 点xと、その点の属する木の根　間の距離を返す
  LL weight_from_root(LL x) {
    root(x); // 経路圧縮
    return diff_weight[x];
  }

  // yの根っこから見た距離-xの根っこから見た距離を返す
  LL weight_diff(LL x, LL y) {
    return weight_from_root(y) - weight_from_root(x);
  }

  bool issame(LL x, LL y) { return root(x) == root(y); }

  bool merge(LL x, LL y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (rank[x] < rank[y])
      swap(x, y);
    if (rank[x] == rank[y])
      rank[x]++;
    parent[y] = x;
    return true;
  }

  // xの属する木、yの属する木を統合 ただしx,y間の距離がwであるとする
  // x,yがすでに同じ木に属しているならfalseを返す
  // weight_from_root(y) - weight_from_root(x) = w となるように merge する
  bool merge(LL x, LL y, LL w) {
    // x と y それぞれについて、 root との重み差分を補正
    // merge(x, y) 操作では、元々の x と y との間に辺を繋ぐのではなく、
    // root(x) と root(y) の間をつなぐので、
    // つなぐべき辺の重みは w ではなく修正が必要になる。
    w += (weight_from_root(x) - weight_from_root(y));
    // xとyのrootを見る(xとyが既につながっていたらfalseを返す)
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    // rank[x] >= rank[y] となるように x と y を swap (それに合わせて w
    // も符号反転する)
    if (rank[x] < rank[y])
      swap(x, y), w = -w;
    // y (のroot) を x (のroot) の下にくっつける
    if (rank[x] == rank[y])
      rank[x]++;
    parent[y] = x;
    // x が y の親になるので、x と y の差分を diff_weight[y] に記録
    diff_weight[y] = w;
    return true;
  }
};

LL N, M;
tuple<LL, LL, LL> LRD[100000];

string ans = "Yes";

void solve() {
  // 例えばleft=4,right=3,distance=1と与えられたならば
  // 点4と点3を同じ木に所属させ、点4と点3の、根からの距離を
  // union findに保存しておく
  UnionFind uf(N);
  for (int i = 0; i < M; i++) {
    // 点leftより点rightが距離distanceだけ離れている、という情報
    LL left = get<0>(LRD[i]);
    LL right = get<1>(LRD[i]);
    LL distance = get<2>(LRD[i]);
    left--, right--;
    // left,rightが同じ木に属しているならば
    if (uf.issame(left, right)) {
      // rightの根っこから見た距離-leftの根っこから見た距離を返す
      LL diff = uf.weight_diff(left, right);
      // left,right間の距離が与えられた距離と一致しなければ
      // 矛盾すると分かる
      if (diff != distance) {
        ans = "No";
        return;
      }
    }
    // left,rightが同じ木に属していないならば統合
    else
      uf.merge(left, right, distance);
  }
}

int main() {
  cin >> N >> M;
  for (int i = 0; i < M; i++) {
    LL l, r, d;
    cin >> l >> r >> d;
    LRD[i] = tuple<LL, LL, LL>(l, r, d);
  }

  solve();

  cout << ans << endl;
  return 0;
}

Fixed submission:
// g++ -std=c++14 test.cpp -o test.out
// 問題URL
// https://atcoder.jp/contests/abc087/tasks/arc090_b

// 重み付きUnion find
// 参考：https://qiita.com/drken/items/cce6fc5c579051e64fab

#include <algorithm>
#include <bitset>
#include <cassert>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits.h>
#include <map>
#include <math.h>
#include <queue>
#include <set>
#include <stdlib.h>
#include <string.h>
#include <utility>
#include <vector>
using namespace std;

#define LL long long int
const LL INF = (LL)(1e18) + 1;
const int INF_INT = 2147483647 - 1e6 - 1;
const LL mod = 1000000007ll;
const int mod_int = 1000000007;

struct UnionFind {
  vector<LL> parent;      // 親ノード
  vector<LL> rank;        // ランク
  vector<LL> diff_weight; // 親ノードとの重みの差分

  UnionFind(LL n = 1) { init(n); }

  void init(LL n = 1) {
    parent.resize(n);
    rank.resize(n);
    diff_weight.resize(n);
    for (LL i = 0; i < n; ++i)
      parent[i] = i, rank[i] = 0, diff_weight[i] = 0;
  }

  // 重みつき Union-Find では、経路圧縮のついでに差分重み diff_weight も更新する
  LL root(LL x) {
    if (parent[x] == x)
      return x;
    else {
      LL r = root(parent[x]);
      diff_weight[x] += diff_weight[parent[x]]; // 累積和をとる
      return parent[x] = r;
    }
  }

  // 点xと、その点の属する木の根　間の距離を返す
  LL weight_from_root(LL x) {
    root(x); // 経路圧縮
    return diff_weight[x];
  }

  // yの根っこから見た距離-xの根っこから見た距離を返す
  LL weight_diff(LL x, LL y) {
    return weight_from_root(y) - weight_from_root(x);
  }

  bool issame(LL x, LL y) { return root(x) == root(y); }

  bool merge(LL x, LL y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (rank[x] < rank[y])
      swap(x, y);
    if (rank[x] == rank[y])
      rank[x]++;
    parent[y] = x;
    return true;
  }

  // xの属する木、yの属する木を統合 ただしx,y間の距離がwであるとする
  // x,yがすでに同じ木に属しているならfalseを返す
  // weight_from_root(y) - weight_from_root(x) = w となるように merge する
  bool merge(LL x, LL y, LL w) {
    // x と y それぞれについて、 root との重み差分を補正
    // merge(x, y) 操作では、元々の x と y との間に辺を繋ぐのではなく、
    // root(x) と root(y) の間をつなぐので、
    // つなぐべき辺の重みは w ではなく修正が必要になる。
    w += (weight_from_root(x) - weight_from_root(y));
    // xとyのrootを見る(xとyが既につながっていたらfalseを返す)
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    // rank[x] >= rank[y] となるように x と y を swap (それに合わせて w
    // も符号反転する)
    if (rank[x] < rank[y])
      swap(x, y), w = -w;
    // y (のroot) を x (のroot) の下にくっつける
    if (rank[x] == rank[y])
      rank[x]++;
    parent[y] = x;
    // x が y の親になるので、x と y の差分を diff_weight[y] に記録
    diff_weight[y] = w;
    return true;
  }
};

LL N, M;
tuple<LL, LL, LL> LRD[200000];

string ans = "Yes";

void solve() {
  // 例えばleft=4,right=3,distance=1と与えられたならば
  // 点4と点3を同じ木に所属させ、点4と点3の、根からの距離を
  // union findに保存しておく
  UnionFind uf(N);
  for (int i = 0; i < M; i++) {
    // 点leftより点rightが距離distanceだけ離れている、という情報
    LL left = get<0>(LRD[i]);
    LL right = get<1>(LRD[i]);
    LL distance = get<2>(LRD[i]);
    left--, right--;
    // left,rightが同じ木に属しているならば
    if (uf.issame(left, right)) {
      // rightの根っこから見た距離-leftの根っこから見た距離を返す
      LL diff = uf.weight_diff(left, right);
      // left,right間の距離が与えられた距離と一致しなければ
      // 矛盾すると分かる
      if (diff != distance) {
        ans = "No";
        return;
      }
    }
    // left,rightが同じ木に属していないならば統合
    else
      uf.merge(left, right, distance);
  }
}

int main() {
  cin >> N >> M;
  for (int i = 0; i < M; i++) {
    LL l, r, d;
    cin >> l >> r >> d;
    LRD[i] = tuple<LL, LL, LL>(l, r, d);
  }

  solve();

  cout << ans << endl;
  return 0;
}