#include <bits/stdc++.h>
using namespace std;
template <class T>
using min_heap = priority_queue<T, vector<T>, greater<T>>;
struct UnionFind {
  vector<int> data;
  int sz;
  UnionFind(int sz = 0) : data(sz, -1), sz(sz) {}
  bool unionSet(int x, int y) {
    if ((x = root(x)) == (y = root(y))) return false;
    if (data[x] > data[y]) swap(x, y);
    data[x] += data[y], data[y] = x, sz--;
    return true;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
  int size() { return sz; }
};
int main() {
  int n, m;
  cin >> n >> m;
  int p, q;
  cin >> p >> q;
  vector<int> x(m), y(m), l(m);
  for (int i = 0; i < m; i++) cin >> x[i] >> y[i] >> l[i], x[i]--, y[i]--;
  UnionFind uf(n);
  for (int i = 0; i < m; i++) uf.unionSet(x[i], y[i]);
  if (uf.size() < q || uf.size() - p > q) return !(cout << "NO" << endl);
  if (m == 0 && n == q && p) return !(cout << "NO" << endl);
  cout << "YES" << endl;
  vector<long long> weight(n);
  for (int i = 0; i < m; i++) weight[uf.root(x[i])] += l[i];
  min_heap<pair<long long, int>> pq;
  for (int i = 0; i < n; i++)
    if (uf.root(i) == i) pq.emplace(weight[i], i);
  for (int _ = 0; _ < p; _++) {
    if ((int)pq.size() > q) {
      auto a = pq.top();
      pq.pop();
      auto b = pq.top();
      pq.pop();
      x.emplace_back(a.second);
      y.emplace_back(b.second);
      long long sum = a.first + b.first;
      long long add = min<int>(sum + 1, 1e9);
      pq.emplace(sum + add, a.second);
    }
    cout << x.back() + 1 << " " << y.back() + 1 << endl;
  }
  return 0;
}
