#include <bits/stdc++.h>
using namespace std;
struct State {
int node;
int lastAttr;
bool operator==(const State &o) const {
return node == o.node && lastAttr == o.lastAttr;
}
};
struct StateHash {
size_t operator()(const State& s) const {
return ((size_t)s.node << 20) ^ s.lastAttr;
}
};
int n, m;
vector<tuple<int,int,int>> g[1000005]; // node -> (to, attr, id)
unordered_map<State, bool, StateHash> visited;
unordered_map<State, bool, StateHash> onStack;
unordered_map<State, State, StateHash> parent;
vector<int> cycle;
bool dfs(State s) {
visited[s] = true;
onStack[s] = true;
int u = s.node;
int lastA = s.lastAttr;
for (auto &[v, attr, eid] : g[u]) {
if (attr == lastA) continue; // attraction must differ
State ns = {v, attr};
if (!visited[ns]) {
parent[ns] = s;
if (dfs(ns)) return true;
} else if (onStack[ns]) {
// cycle found, reconstruct cycle
cycle.clear();
cycle.push_back(eid);
State cur = s;
while (!(cur == ns)) {
auto p = parent[cur];
int from = p.node;
int attrUsed = cur.lastAttr;
int to = cur.node;
// find edge id from p.node to cur.node with attraction cur.lastAttr
for (auto &[to2, attr2, eid2] : g[from]) {
if (to2 == to && attr2 == attrUsed) {
cycle.push_back(eid2);
break;
}
}
cur = p;
}
reverse(cycle.begin(), cycle.end());
return true;
}
}
onStack[s] = false;
return false;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++) g[i].clear();
visited.clear();
onStack.clear();
parent.clear();
for (int i = 1; i <= m; i++) {
int x, y, c; cin >> x >> y >> c;
g[x].emplace_back(y, c, i);
}
bool found = false;
for (int i = 1; i <= n && !found; i++) {
State start = {i, 0}; // lastAttr = 0 means no last attraction yet
if (!visited[start]) {
if (dfs(start)) {
found = true;
}
}
}
if (!found) {
cout << "NO\n";
} else {
cout << "YES\n";
// cout << (int)cycle.size() << "\n";
for (int id : cycle) cout << id << " ";
cout << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t; cin >> t;
while (t--) {
solve();
}
return 0;
}
I2luY2x1ZGUgPGJpdHMvc3RkYysrLmg+CnVzaW5nIG5hbWVzcGFjZSBzdGQ7CgpzdHJ1Y3QgU3RhdGUgewogICAgaW50IG5vZGU7CiAgICBpbnQgbGFzdEF0dHI7CiAgICBib29sIG9wZXJhdG9yPT0oY29uc3QgU3RhdGUgJm8pIGNvbnN0IHsKICAgICAgICByZXR1cm4gbm9kZSA9PSBvLm5vZGUgJiYgbGFzdEF0dHIgPT0gby5sYXN0QXR0cjsKICAgIH0KfTsKCnN0cnVjdCBTdGF0ZUhhc2ggewogICAgc2l6ZV90IG9wZXJhdG9yKCkoY29uc3QgU3RhdGUmIHMpIGNvbnN0IHsKICAgICAgICByZXR1cm4gKChzaXplX3Qpcy5ub2RlIDw8IDIwKSBeIHMubGFzdEF0dHI7CiAgICB9Cn07CgppbnQgbiwgbTsKdmVjdG9yPHR1cGxlPGludCxpbnQsaW50Pj4gZ1sxMDAwMDA1XTsgLy8gbm9kZSAtPiAodG8sIGF0dHIsIGlkKQp1bm9yZGVyZWRfbWFwPFN0YXRlLCBib29sLCBTdGF0ZUhhc2g+IHZpc2l0ZWQ7CnVub3JkZXJlZF9tYXA8U3RhdGUsIGJvb2wsIFN0YXRlSGFzaD4gb25TdGFjazsKdW5vcmRlcmVkX21hcDxTdGF0ZSwgU3RhdGUsIFN0YXRlSGFzaD4gcGFyZW50Owp2ZWN0b3I8aW50PiBjeWNsZTsKCmJvb2wgZGZzKFN0YXRlIHMpIHsKICAgIHZpc2l0ZWRbc10gPSB0cnVlOwogICAgb25TdGFja1tzXSA9IHRydWU7CiAgICBpbnQgdSA9IHMubm9kZTsKICAgIGludCBsYXN0QSA9IHMubGFzdEF0dHI7CgogICAgZm9yIChhdXRvICZbdiwgYXR0ciwgZWlkXSA6IGdbdV0pIHsKICAgICAgICBpZiAoYXR0ciA9PSBsYXN0QSkgY29udGludWU7IC8vIGF0dHJhY3Rpb24gbXVzdCBkaWZmZXIKCiAgICAgICAgU3RhdGUgbnMgPSB7diwgYXR0cn07CiAgICAgICAgaWYgKCF2aXNpdGVkW25zXSkgewogICAgICAgICAgICBwYXJlbnRbbnNdID0gczsKICAgICAgICAgICAgaWYgKGRmcyhucykpIHJldHVybiB0cnVlOwogICAgICAgIH0gZWxzZSBpZiAob25TdGFja1tuc10pIHsKICAgICAgICAgICAgLy8gY3ljbGUgZm91bmQsIHJlY29uc3RydWN0IGN5Y2xlCiAgICAgICAgICAgIGN5Y2xlLmNsZWFyKCk7CiAgICAgICAgICAgIGN5Y2xlLnB1c2hfYmFjayhlaWQpOwogICAgICAgICAgICBTdGF0ZSBjdXIgPSBzOwogICAgICAgICAgICB3aGlsZSAoIShjdXIgPT0gbnMpKSB7CiAgICAgICAgICAgICAgICBhdXRvIHAgPSBwYXJlbnRbY3VyXTsKICAgICAgICAgICAgICAgIGludCBmcm9tID0gcC5ub2RlOwogICAgICAgICAgICAgICAgaW50IGF0dHJVc2VkID0gY3VyLmxhc3RBdHRyOwogICAgICAgICAgICAgICAgaW50IHRvID0gY3VyLm5vZGU7CgogICAgICAgICAgICAgICAgLy8gZmluZCBlZGdlIGlkIGZyb20gcC5ub2RlIHRvIGN1ci5ub2RlIHdpdGggYXR0cmFjdGlvbiBjdXIubGFzdEF0dHIKICAgICAgICAgICAgICAgIGZvciAoYXV0byAmW3RvMiwgYXR0cjIsIGVpZDJdIDogZ1tmcm9tXSkgewogICAgICAgICAgICAgICAgICAgIGlmICh0bzIgPT0gdG8gJiYgYXR0cjIgPT0gYXR0clVzZWQpIHsKICAgICAgICAgICAgICAgICAgICAgICAgY3ljbGUucHVzaF9iYWNrKGVpZDIpOwogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBjdXIgPSBwOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldmVyc2UoY3ljbGUuYmVnaW4oKSwgY3ljbGUuZW5kKCkpOwogICAgICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgICB9CiAgICB9CiAgICBvblN0YWNrW3NdID0gZmFsc2U7CiAgICByZXR1cm4gZmFsc2U7Cn0KCnZvaWQgc29sdmUoKSB7CiAgICBjaW4gPj4gbiA+PiBtOwogICAgZm9yIChpbnQgaSA9IDE7IGkgPD0gbjsgaSsrKSBnW2ldLmNsZWFyKCk7CiAgICB2aXNpdGVkLmNsZWFyKCk7CiAgICBvblN0YWNrLmNsZWFyKCk7CiAgICBwYXJlbnQuY2xlYXIoKTsKCiAgICBmb3IgKGludCBpID0gMTsgaSA8PSBtOyBpKyspIHsKICAgICAgICBpbnQgeCwgeSwgYzsgY2luID4+IHggPj4geSA+PiBjOwogICAgICAgIGdbeF0uZW1wbGFjZV9iYWNrKHksIGMsIGkpOwogICAgfQoKICAgIGJvb2wgZm91bmQgPSBmYWxzZTsKICAgIGZvciAoaW50IGkgPSAxOyBpIDw9IG4gJiYgIWZvdW5kOyBpKyspIHsKICAgICAgICBTdGF0ZSBzdGFydCA9IHtpLCAwfTsgLy8gbGFzdEF0dHIgPSAwIG1lYW5zIG5vIGxhc3QgYXR0cmFjdGlvbiB5ZXQKICAgICAgICBpZiAoIXZpc2l0ZWRbc3RhcnRdKSB7CiAgICAgICAgICAgIGlmIChkZnMoc3RhcnQpKSB7CiAgICAgICAgICAgICAgICBmb3VuZCA9IHRydWU7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9CgogICAgaWYgKCFmb3VuZCkgewogICAgICAgIGNvdXQgPDwgIk5PXG4iOwogICAgfSBlbHNlIHsKICAgICAgICBjb3V0IDw8ICJZRVNcbiI7CiAgICAgICAvLyBjb3V0IDw8IChpbnQpY3ljbGUuc2l6ZSgpIDw8ICJcbiI7CiAgICAgICAgZm9yIChpbnQgaWQgOiBjeWNsZSkgY291dCA8PCBpZCA8PCAiICI7CiAgICAgICAgY291dCA8PCAiXG4iOwogICAgfQp9CgppbnQgbWFpbigpIHsKICAgIGlvczo6c3luY193aXRoX3N0ZGlvKGZhbHNlKTsKICAgIGNpbi50aWUobnVsbHB0cik7CgogICAgaW50IHQ7IGNpbiA+PiB0OwogICAgd2hpbGUgKHQtLSkgewogICAgICAgIHNvbHZlKCk7CiAgICB9CiAgICByZXR1cm4gMDsKfQo=