import std.stdio, std.conv, std.string, std.array, std.range, std.algorithm, std.container;
import std.math, std.random, std.bigint, std.datetime, std.format;
void main(string[] args){ if(args.length > 1) if(args[1] == "-debug") DEBUG = 1; solve(); }
void log()(){ writeln(""); } void log(T, A ...)(T t, lazy A a){ if(DEBUG) write(t, " "), log(a); } bool DEBUG = 0; 
string read(){ static string[] ss; while(!ss.length) ss = readln.chomp.split; string res = ss[0]; ss.popFront; return res; }
T rtype(T)(){ return read.to!T; } alias rint = rtype!int, rlong = rtype!long, rreal = rtype!real;
T[] rtypes(T)(int n){ return n.iota.map!(i => rtype!T()).array; } alias rint = rtypes!int, rlong = rtypes!long, rreal = rtypes!real;
T[][] rtypess(T)(int n, int m){ return n.iota.map!(i => rtypes!T(m)).array; } alias rint = rtypess!int, rlong = rtypess!long, rreal = rtypess!real;

// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- //

void solve(){
	int n = rint;
	long m = rlong;
	
	Node[] nodes;
	foreach(i; 0 .. n) nodes ~= new Node(i, 0);
	foreach(nd; nodes) nd.isFree = 1;
	log("nodes:", nodes);
	
	Edge[] edges;
	int q = rint;
	foreach(_; 0 .. q){
		int a = rint, b = rint, c = rint;
		edges ~= new Edge(nodes[a], nodes[b], c);
	}
	
	edges.sort!"a.value>b.value"();
		// Type 1 edges ("has more than one paths") come first
	log("edges:", edges);
	
	foreach(ed; edges){
		ed.node1.isFree = 0, ed.node2.isFree = 0;
		if(ed.value == 0){ // has only one path
			if(ed.node1.group.id == ed.node2.group.id){
				"No".writeln;
				return;
			}
		}
		else{ // has more than one paths
			if(ed.node1.group.id != ed.node2.group.id){
				ed.node1.group.eat(ed.node2.group);
			}
		}
	}
	log("Now after eating, nodes:", nodes);
	
	long freecount;
	foreach(nd; nodes) if(nd.isFree) freecount += 1;
	log("freecount:", freecount);
	
	long minedgecount;
	long groupcount;
	long maxorder;
	Group maxgroup;
	foreach(nd; nodes){
		log("nd:", nd);
		if(nd.group.id != nd.id) continue;
		if(nd.isFree) continue;
		
		groupcount += 1;
		log("groupcount:", groupcount);
		
		long u = nd.group.nodes.length;
		log("u:", u);
		if(u == 2){
			if(freecount > 0){
				freecount -= 1;
				u = 3;
			}
			else{
				"No".writeln;
				return;
			}
		}
		
		if(u > 1) minedgecount += u;
		if(maxorder < u) maxorder = u, maxgroup = nd.group;
		log("maxorder:", maxorder, "maxgroup:", maxgroup);
		nd.group.value = u;
	}
	
	minedgecount += groupcount - 1;
	log("minedgecount:", minedgecount);
	
	if(minedgecount > m){
		"No".writeln;
		return;
	}
	
	long maxedgecount = groupcount - 1;
	foreach(nd; nodes){
		log("nd:", nd);
		if(nd.group.id != nd.id) continue;
		if(nd.isFree) continue;
		long u = nd.group.value;
		log("u:", u);
		if(nd.group.id == maxgroup.id) u += freecount;
		maxedgecount += u * (u - 1) / 2;
		log("maxedgecount:", maxedgecount);
	}
	
	if(maxedgecount < m){
		"No".writeln;
		return;
	}
	
	"Yes".writeln;
	
}

// Union-find ※多分使いやすいやつ
class Edge{
	Node node1;
	Node node2;
	long value;
	this(Node node1, Node node2, long value){
		this.node1 = node1, this.node2 = node2;
		this.value = value;
	}
	override string toString(){
		return [node1.id, node2.id, value].to!string;
	}
}

class Node{
	long id;
	long value;
	bool isFree;
	Group group;
	this(long id, long value){
		this.id = id;
		this.value = value;
		this.group = new Group(this);
	}
	override string toString(){
		return [id, group.id, value].to!string;
	}
}

class Group{
	long value;
	long pendingEdgeCount;
	long id;
	long edgecount;
	Node[] nodes;
	this(Node node){
		this.id = node.id;
		this.nodes = [node];
		this.value = node.value;
	}
	void eat(Group gp){
		if(gp.nodes.length > this.nodes.length) gp.eat(this);
		else{
			foreach(nd; gp.nodes){
				this.nodes ~= nd;
				nd.group = this;
				this.value += nd.value;
			}
			this.edgecount += gp.edgecount;
			this.pendingEdgeCount += gp.pendingEdgeCount;
		}
	}
}
