import java.io.*;
import java.util.*;

public class DoAll implements Runnable {

	private final int MAXN = 100000;
	private final int MAXM = 200000;
	private int testNum;
	private PrintWriter testWriter;
	private PrintWriter ansWriter;
	private Random rnd;
	private ArrayList<Road>[] s;
	private HashSet<Road> roadsSet;
	private ArrayList<Road> roadsList;
	private int n, m;
	private int maxA;
	private int minA;
	private boolean[] mark;
	private final String defaultMessage = "wrong input format.";
	
	public void run() {
		try {
			doall();
		} catch (IOException e) {
			throw new Error();
		}
	}

	private void doall() throws IOException {
		initialize();
		
		Scanner in = new Scanner(new FileReader("tests.t"));
		while (in.hasNext()) {
			open();
			clear();
			n = in.nextInt();
			int mm = in.nextInt();
			int a = in.nextInt();
			int b = in.nextInt();
			assertBounds(n, 1, MAXN, "n");
			assertBounds(mm, 0, MAXM, "m");
			assertStatement(a + b == n - 1, "a + b != n - 1.");
			for (int i = 0; i < mm; i++) {
				int u = in.nextInt() - 1;
				int v = in.nextInt() - 1;
				int t = in.nextInt();
				assertStatement(addRoad(u, v, t), "");
			}
			calcAns();
			writeAns(a);
		}
		
		n = 100;
		
		open();
		clear();
		genRandomTree(0.40);
		calcAns();
		assertStatement(minA == maxA, "internal error.");
		writeAns(minA);
		
		open();
		clear();
		genRandomTree(0.60);
		calcAns();
		writeAns(minA + 1);


//		open();
//		clear();
//		genRandomTree(0.50);
//		calcAns();
//		writeAns(minA - 1);

		
		open();
		clear();
		genRandomTree(0.05);
		for (int i = 0; i < 200; i++) {
			addRandomRoad(0.00);
		}
		calcAns();
		writeAns(maxA);
		
		open();
		clear();
		genChain(0, n - 1, 0.5);
		calcAns();
		writeAns(minA);
		
		open();
		clear();
		genChain(0, n - 1, 1.0);
		for (int i = 0; i < 500; i++) {
			addRandomRoad(0.0);
		}
		calcAns();
		writeAns(n - 1);
		


//		n = 1000;
//		
//		open();
//		clear();
//		genRandomTree(0.5);
//		for (int i = 0; i < 100000; i++) {
//			addRandomRoad(0.5);
//		}
//		calcAns();
//		writeAns((minA + maxA) / 2);

		
		
		
		n = 10000;
		
		open();
		clear();
		genGroupsTest(100);
		calcAns();
		assertStatement(minA == maxA, "internal error.");
		writeAns(minA);
		
//		open();
//		clear();
//		genGroupsTest(10);
//		calcAns();
//		assertStatement(minA == maxA, "internal error.");
//		writeAns(minA);
//
//		open();
//		clear();
//		genGroupsTest(1000);
//		calcAns();
//		assertStatement(minA == maxA, "internal error.");
//		writeAns(minA);
//
//		
//		open();
//		clear();
//		genRandomTree(0.0);
//		for (int i = 0; i < 5 * n; i++) {
//			addRandomRoad(0.0);
//		}
//		calcAns();
//		assertStatement(minA == maxA, "internal error.");
//		writeAns(minA);
		
		
		open();
		clear();
		genRandomTree(1.0);
		for (int i = 0; i < 5 * n; i++) {
			addRandomRoad(1.0);
		}
		calcAns();
		assertStatement(minA == maxA, "internal error.");
		writeAns(minA);

		n = 300;
		
		open();
		clear();
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) if (i != j) {
				int t = rnd.nextBoolean() ? 1 : 0;
				addRoad(i, j, t);
			}
		}
		shuffleAll();
		calcAns();
		writeAns(n / 2);
		

//		open();
//		clear();
//		int k = 14;
//		n = (1 << k) - 1;
//		genHeapTest(k, 1);
//		for (int i = (1 << (k - 1)) - 1; i < n; i++) {
//			int j = rnd.nextInt(1 << (k - 1)) + (1 << (k - 1)) - 1;
//			addRoad(i, j, 0);
//		}
//		calcAns();
//		writeAns(0);

		
		open();
		clear();
		int k = 16;
		n = (1 << k) - 1;
		genHeapTest(k, 0);
		for (int i = (1 << (k - 1)) - 1; i < n; i++) {
			int j = rnd.nextInt(1 << (k - 1)) + (1 << (k - 1)) - 1;
			addRoad(i, j, 1);
		}
		shuffleAll();
		calcAns();
		writeAns(n - 1);
		

//		n = MAXN - 1;
//		
//		open();
//		clear();
//		genGroupsTest(3);
//		calcAns();
//		writeAns(minA);
//
//		n = MAXN;
//		
//		open();
//		clear();
//		genGroupsTest(5);
//		calcAns();
//		writeAns(minA + 1);

		

//		open();
//		clear();
//		k = 9;
//		int ttt = 1;
//		for (int i = 0; i < n - 1; i += k) {
//			ttt = 1 - ttt;
//			genTree(i, i + k + 1, ttt);
//		}
//		calcAns();
//		shuffleAll();
//		writeAns(maxA);
//		
//		open();
//		clear();
//		genTree(0, n / 2 + 1, 0);
//		genTree(n / 2, n, 1);
//		calcAns();
//		writeAns(minA);


		
		open();
		clear();
		genRandomTree(0.5);
		for (int i = 0; i < MAXN + 1; i++) {
			addRandomRoad(0.5);
		}
		shuffleAll();
		calcAns();
		writeAns((minA + maxA) / 2);
		

//		open();
//		clear();
//		genRandomTree(0.5);
//		for (int i = 0; i < MAXN + 1; i++) {
//			addRandomRoad(0.5);
//		}
//		shuffleAll();
//		calcAns();
//		writeAns(minA - 1);

		
		open();
		clear();
		genRandomTree(0.5);
		for (int i = 0; i < MAXN + 1; i++) {
			addRandomRoad(0.5);
		}
		shuffleAll();
		calcAns();
		writeAns(maxA + 1);
		

//		open();
//		clear();
//		genRandomTree(0.01);
//		for (int i = 0; i < MAXN + 1; i++) {
//			addRandomRoad(0.0);
//		}
//		shuffleAll();
//		calcAns();
//		writeAns(maxA);
//
//		open();
//		clear();
//		genRandomTree(0.99);
//		for (int i = 0; i < MAXN + 1; i++) {
//			addRandomRoad(1.0);
//		}
//		shuffleAll();
//		calcAns();
//		writeAns(minA - 1);

		
		open();
		clear();
		for (int i = 1; i < n; i++) {
			for (int z = 0; z < 1; z++) {
				int j = rnd.nextInt(i);
				addRoad(i, j, j & 1);
			}
		}
		calcAns();
		writeAns(minA);
		

//		open();
//		clear();
//		genRandomTree(1.0);
//		genRandomTree(0.0);
//		calcAns();
//		writeAns(0);
//		
//		open();
//		clear();
//		genRandomTree(1.0);
//		genRandomTree(0.0);
//		calcAns();
//		writeAns(n - 1);
//		
//		open();
//		clear();
//		n = (1 << 16) - 1;
//		genHeapTest(15, 1);
//		shuffleAll();
//		genHeapTest(16, 0);
//		shuffleAll();
//		calcAns();
//		writeAns(minA - 1);
//
//		open();
//		clear();
//		n = (1 << 16) - 1;
//		genHeapTest(15, 0);
//		shuffleAll();
//		genHeapTest(16, 1);
//		shuffleAll();
//		calcAns();
//		writeAns(0);

		n = MAXN;
		
		open();
		clear();
		genForest(100, 1);
		shuffleAll();
		genRandomTree(1.00);
		while (m < MAXM) {
			addRandomRoad(1.00);
		}
		calcAns();
		writeAns(minA);

		open();
		clear();
		genForest(10, 0);
		shuffleAll();
		genRandomTree(0.00);
		while (m < MAXM) {
			addRandomRoad(0.00);
		}
		calcAns();
		writeAns(maxA);

		n = MAXN / 10;
		
		open();
		clear();
		genGroups(100, 1);
		shuffleAll();
		genRandomTree(1.00);
		shuffleAll();
		while (m < MAXM) {
			addRandomRoad(1.00);
		}
		calcAns();
		writeAns(minA);
	}

	private void genGroups(int groupSize, int t) {
		for (int i = 0; i < n / groupSize; i++) {
			genTree(i * groupSize, (i + 1) * groupSize, 1 - t);
			for (int j = 0; j < groupSize; j++) {
				int u = rnd.nextInt(groupSize);
				int v = rnd.nextInt(groupSize);
				if (u == v) {
					continue;
				}
				addRoad(u + i * groupSize, v + i * groupSize, 1 - t);
			}
		}
	}

	private void genForest(int treeSize, int t) {
		for (int i = 0; i < n / treeSize; i++) {
			genTree(i * treeSize, (i + 1) * treeSize, 1 - t);
		}
	}
	
	private void genHeapTest(int k, int t) {
		for (int i = 0; i < (1 << (k - 1)) - 1; i++) {
			addRoad(i, 2 * i + 1, t);
			addRoad(i, 2 * i + 2, t);
		}
	}

	private void genGroupsTest(int groupSize) {
		int groupsNum = n / groupSize;
		genTree(0, groupsNum, 0.5);
		int[] perm = new int[n];
		for (int i = 0; i < groupsNum; i++) {
			perm[i] = groupSize * i;
		}
		int tt = 0;
		for (int i = groupsNum; i < n; i++) {
			while (tt % groupSize == 0) {
				tt++;
			}
			perm[i] = tt;
			tt++;
		}
		permNodes(perm);
		
		for (int i = 0; i < groupsNum; i++) {
			int t = rnd.nextBoolean() ? 1 : 0;
			genTree(i * groupSize, (i + 1) * groupSize, t);
			for (int j = 0; j < groupSize; j++) {
				int u = rnd.nextInt(groupSize);
				int v = rnd.nextInt(groupSize);
				if (u == v) {
					continue;
				}
				addRoad(u + i * groupSize, v + i * groupSize, 1 - t);
			}
		}
		
		shuffleAll();
	}

	// from lo inclusive to hi exclusive.
	private void genTree(int lo, int hi, double d) {
		for (int i = hi - 1; i > lo; i--) {
			int j = rnd.nextInt(i - lo) + lo;
			int t = (rnd.nextDouble() > d) ? 1 : 0;
			addRoad(i, j, t);
		}
	}

	private void genChain(int lo, int hi, double d) {
		for (int i = lo; i < hi; i++) {
			int t = (rnd.nextDouble() > d) ? 1 : 0;
			addRoad(i, i + 1, t);
		}
	}

	private void addRandomRoad(double d) {
		while (true) {
			int u = rnd.nextInt(n);
			int v = rnd.nextInt(n);
			if (u == v) {
				continue;
			}
			int t = (rnd.nextDouble() > d) ? 1 : 0;
			if (addRoad(u, v, t)) {
				break;
			}
		}
	}

	private void shuffleAll() {
		shuffleEdges();
		shuffleNodes();
	}

	private void shuffleNodes() {
		int[] perm = new int[n];
		for (int i = 0; i < n; i++) {
			perm[i] = i;
		}
		for (int i = n - 1; i > 0; i--) {
			int j = rnd.nextInt(i + 1);
			int tmp = perm[i];
			perm[i] = perm[j];
			perm[j] = tmp;
		}
		
		permNodes(perm);
	}

	private void permNodes(int[] perm) {
		for (int i = 0; i < n; i++) {
			s[i].clear();
		}
		roadsSet.clear();
		for (Road e : roadsList) {
			e.u = perm[e.u];
			e.v = perm[e.v];
			e.update();
			s[e.u].add(e);
			s[e.v].add(e);
			roadsSet.add(e);
		}
	}

	private void shuffleEdges() {
		for (int i = m - 1; i > 0; i--) {
			int j = rnd.nextInt(i + 1);
			Road tmp = roadsList.get(i);
			roadsList.set(i, roadsList.get(j));
			roadsList.set(j, tmp);
		}
	}

	private void genRandomTree(double d) {
		genTree(0, n, d);
		shuffleAll();
	}

	public static void main(String[] args) {
		new Thread(new DoAll()).start();
	}

	private void writeAns(int a) {
		assertBounds(n, 1, MAXN, "n");
		assertBounds(m, 0, MAXM, "m");
		boolean ans = ((a >= minA) && (a <= maxA));
		ansWriter.println(ans ? "yes" : "no");
		ansWriter.close();
		
		testWriter.println(n + " " + m + " " + a + " " + (n - 1 - a));
		for (int i = 0; i < m; i++) {
			Road r = roadsList.get(i);
			testWriter.println((r.u + 1) + " " + (r.v + 1) + " " + r.t);
		}
		testWriter.close();
		System.out.println("OK");
	}

	private void calcAns() {
		// Check connectedness
		Arrays.fill(mark, false);
		dfs0(0);
		for (int i = 0; i < n; i++) {
			assertStatement(mark[i], "graph is not connected.");
		}
		
		maxA = n;
		minA = -1;
		Arrays.fill(mark, false);
		for (int i = 0; i < n; i++) {
			if (!mark[i]) {
				dfs(0, i);
				maxA--;
			}
		}
		Arrays.fill(mark, false);
		for (int i = 0; i < n; i++) {
			if (!mark[i]) {
				dfs(1, i);
				minA++;
			}
		}
	}

	private void dfs(int t, int u) {
		mark[u] = true;
		for (Road r : s[u]) {
			if (r.t != t) {
				continue;
			}
			int v = r.u ^ r.v ^ u;
			if (!mark[v]) {
				dfs(t, v);
			}
		}
	}

	private void dfs0(int u) {
		mark[u] = true;
		for (Road r : s[u]) {
			int v = r.u ^ r.v ^ u;
			if (!mark[v]) {
				dfs0(v);
			}
		}
	}

	private boolean addRoad(int u, int v, int t) {
		if (u == v) {
			return false;
		}
		Road r = new Road(u, v, t);
		if (roadsSet.contains(r)) {
			return false;
		}
		roadsSet.add(r);
		roadsList.add(r);
		s[u].add(r);
		s[v].add(r);
		m++;
		return true;
	}

	@SuppressWarnings("unchecked")
	private void initialize() {
		roadsSet = new HashSet<Road>();
		roadsList = new ArrayList<Road>();
		s = new ArrayList[MAXN];
		for (int i = 0; i < MAXN; i++) {
			s[i] = new ArrayList<Road>();
		}
		mark = new boolean[MAXN];
		testNum = 0;
		rnd = new Random(239239239239239l);
	}
	
	private class Road {
		private int u, v, t;
		private int min;
		private int max;

		public Road(int u, int v, int t) {
			this.u = u;
			this.v = v;
			this.t = t;
			update();
		}
		
		public void update() {
			if (u < v) {
				min = u;
				max = v;
			} else {
				max = u;
				min = v;
			}
		}
		
		@Override
		public int hashCode() {
			return min * 239 + max * 23 + t * 53;
		}
		
		@Override
		public boolean equals(Object obj) {
			Road that = (Road) obj;
			return (that.min == min) && (that.max == max) && (that.t == t);
		}
	}

	private void open() throws IOException {
		testNum++;
		String testStr = Integer.valueOf(testNum).toString();
		if (testNum < 10) testStr = "0" + testStr;
		testWriter = new PrintWriter(testStr);
		ansWriter = new PrintWriter(testStr + ".a");
		System.out.print("Generating test # " + testStr + " ...   ");
	}

	private void clear() {
		for (ArrayList<Road> l : s) {
			l.clear();
		}
		roadsSet.clear();
		roadsList.clear();
		m = 0;
	}

	private void assertBounds(int n, int lo, int hi, String varName) {
		assertStatement(n >= lo && n <= hi, varName + " is out of bounds.");
	}

	private void assertStatement(boolean st, String msg) {
		if (msg == null) {
			msg = defaultMessage;
		}
		msg = "ERROR: " + msg;
		if (!st) {
			System.out.println(msg);
			throw new Error(msg);
		}
	}

}
