#include <bits/stdc++.h>
using namespace std;

#define mp make_pair
#define mt make_tuple
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define ALL(a) begin(a), end(a)
#define SZ(a) ((int)(a).size())

#define REP(a, b) for (int a = 0; a < (b); a++)
#define FOR(a, b, c) for (int a = (b); a <= (c); a++)
#define REPD(a, b) for (int a = (b) - 1; a >= 0; a--)
#define FORD(a, b, c) for (int a = (b); a >= (c); a--)

#define MIN(a, b) (a) = min((a), (b))
#define MAX(a, b) (a) = max((a), (b))

#if defined(__GNUC__)
#define LIKELY(x) (__builtin_expect((x), 1))
#define UNLIKELY(x) (__builtin_expect((x), 0))
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif  // defined(__GNUC__)

#ifdef __DEBUG
#define debug if (true)
#else
#define debug if (false)
#endif  // __DEBUG

template <typename... T>
using tpl = tuple<T...>;

#define DECLARE_TUPLE_ACCESSOR(TIDX) \
  struct TupleIndex_##TIDX##_t {};\
  template <typename... T>\
  typename tuple_element<TIDX, tuple<T...>>::type& operator/(tuple<T...>& t, TupleIndex_##TIDX##_t) { return get<TIDX>(t); }\
  template <typename... T>\
  const typename tuple_element<TIDX, tuple<T...>>::type& operator/(const tuple<T...>& t, TupleIndex_##TIDX##_t) { return get<TIDX>(t); }\
  constexpr TupleIndex_##TIDX##_t _##TIDX;

DECLARE_TUPLE_ACCESSOR(0)
DECLARE_TUPLE_ACCESSOR(1)
DECLARE_TUPLE_ACCESSOR(2)
DECLARE_TUPLE_ACCESSOR(3)

#undef DECLARE_TUPLE_ACCESSOR

using ll = long long;
using pii = pair<int, int>;
using vi = vector<int>;

template <typename T> using V = vector<T>;
template <typename T> using VV = vector<V<T>>;
template <typename T> using VVV = vector<VV<T>>;

template <typename T1, typename T2>
istream& operator>>(istream& is, pair<T1, T2> &p) {
  return is >> p.fi >> p.se;
}

#if __cplusplus > 201402L
template <typename... T, size_t... I>
istream& tupleInputImpl(istream &is, tuple<T...> &tpl, index_sequence<I...>) {
  return (is >> ... >> get<I>(tpl));
}

template <typename... T>
istream& operator>>(istream& is, tuple<T...> &tpl) {
  return tupleInputImpl(is, tpl, index_sequence_for<T...>{});
}
#endif  // __cplusplus > 201402L

struct DisjointSet {
  vector<int> pset, setSize;
  int setNum;

  DisjointSet() {}
  DisjointSet(int n) {
    initSet(n);
  }

  void initSet(int n) {
    pset.resize(n);
    setSize.assign(n, 1);
    setNum = n;
    for (int i = 0; i < n; i++) pset[i] = i;
  }

  int find(int n) { return pset[n] == n ? n : (pset[n] = find(pset[n])); }

  bool isSameSet(int i, int j) { return find(i) == find(j); }

  void merge(int i, int j) {
    if(isSameSet(i, j)) return;
    setNum--;
    i = find(i);
    j = find(j);
    if(setSize[i] < setSize[j]) {
      pset[i] = j;
      setSize[j] += setSize[i];
    } else {
      pset[j] = i;
      setSize[i] += setSize[j];
    }
  }
};

void Main() {
  int n, q;
  ll m;
  cin >> n >> m >> q;
  vector<pii> diffQuery;
  DisjointSet ds(n);
  REP(i, q) {
    int a, b, c;
    cin >> a >> b >> c;
    if (c == 0) ds.merge(a, b);
    else diffQuery.eb(a, b);
  }
  for (auto &it : diffQuery) {
    if (ds.isSameSet(it.fi, it.se)) {
      cout << "No" << endl;
      return;
    }
  }
  ll mn = diffQuery.empty() ? (n - 1) : n;
  ll mx = n - ds.setNum + 1LL * ds.setNum * (ds.setNum - 1) / 2;
  if (m < mn || m > mx) {
    cout << "No" << endl;
    return;
  }
  cout << "Yes" << endl;
}

int main(int argc, char *argv[]) {
  if (argc > 1) {
    freopen(argv[1], "r", stdin);
  } else {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
  }
  Main();
}


Measured memory_bytes: 2168