#include using namespace std; using ll = long long; using ld = long double; using pii = pair; using vi = vector; using vvi = vector; #define rep(i, a, b) for(ll i = (a); i < (b); i++) #define all(x) begin(x),end(x) #define sz(x) (int)(x).size() struct UnionFind { vi p, q; UnionFind(int n) : p(n, -1), q(n, 0) {} int find(int u) { return p[u] < 0 ? u : p[u] = find(p[u]); } bool join(int u, int v) { u = find(u); v = find(v); if (u == v) return false; // no smaller into larger p[u] += p[v]; q[u] += q[v]; p[v] = u; return true; } }; void solve() { ll n, m; cin >> n >> m; vvi a(n); for (auto& x : a) { ll k; cin >> k; x.resize(k); for (auto& z : x) cin >> z, --z, z += n; } vi order(n); iota(all(order), 0ll); ranges::sort(order, {}, [&] (ll x) { return ssize(a[x]); }); UnionFind uf(n + m); rep(i,n,n+m) uf.q[i] = 1; for (ll u : order) { ll unmatched = 0; ll matchsize = 0; set st; for (ll z : a[u]) { if (uf.find(z) == z) unmatched++; else st.insert(uf.find(z)); } for (auto z : st) matchsize += uf.q[z]; // matchsize + unmatched must be size if (matchsize + unmatched == ssize(a[u])) { for (ll z : a[u]) { uf.join(u, z); } } else { // We need to find the violating node set s(all(a[u])); rep(v,0,n) { if (u == v) continue; set t(all(a[v])); vi gah(2); if (ssize(s) >= ssize(t)) { for (ll z : a[v]) gah[s.contains(z)] = true; } else { for (ll z : a[u]) gah[t.contains(z)] = true; } if (gah[0] && gah[1]) { cout << "YES\n" << u + 1 << ' ' << v + 1 << '\n'; exit(0); } } } } cout << "NO\n"; } int main() { cin.tie(0)->sync_with_stdio(0); cin.exceptions(cin.failbit); //ll t; cin >> t; while(t--) solve(); }