// #define DEBUG
int TIMER=20;
#define TIME if(!TIMER--)exit(0);
#pragma GCC optimize("O3")
#include <iostream>
#include <vector>
using namespace std; const int mx=1000000; typedef pair<int,int> pi;
#define pb push_back
#define f first
#define s second
pi s1[mx],s2[mx]; bool sz1[mx],edge[mx]; vector<int> adj0[mx]; //initial sets
pi e[mx],es[mx]; vector<int> adj[mx]; //graph
int cntE,cntV; bool vis[mx]; //components
void dfs1(int c)
{
if(vis[c]) return; cntV++, cntE+=adj[c].size(), vis[c]=1;
for(int t:adj[c]) dfs1(e[t].f+e[t].s-c);
}
bool cycle[mx],cycledone; int cpar[mx],rt;
void dfs2(int c, int p)
{
for(int t:adj[c]) if(t!=p)
{
int k=e[t].f+e[t].s-c; if(cpar[k]==-1) cpar[k]=c, dfs2(k,t); else if(!cycledone)
{rt=c,cycledone=1; while(1) {cycle[rt]=1; if(rt==k) break; rt=cpar[rt];}}
}
}
bool vis3[mx]; int cnt3;
void dfs3(int c, int p)
{
if(vis3[c]) return; vis3[c]=1; for(int t:adj[c])
{
int k=e[t].f+e[t].s-c; if(k==p) continue; dfs3(k,c);
if(!cycle[k]) cnt3+=k==es[t].f||k==es[t].s;
}
}
vector<int> cv, cn;
void getcycle(int c)
{
cn.pb(c); if(c==rt&&!cv.empty()) return; for(int t:adj[c])
{
int k=e[t].f+e[t].s-c;
if(cycle[k]&&!(!cv.empty()&&t==cv.back())) {cv.pb(t), getcycle(k); break;}
}
}
int solve()
{
#ifdef DEBUG
cout<<"Start\n"<<flush;
#endif
//note: swap m/n
int m,n,ans=0; cin>>m>>n;
for(int i=0; i<n; i++) adj0[i].clear(),adj[i].clear(), vis[i]=vis3[i]=0, cycle[i]=0, cpar[i]=-1;
for(int i=0; i<m; i++) edge[i]=1;
//input and delete |T|=1
{
int a,b;
for(int i=0; i<m; i++) {int c; cin>>c>>a; if(c==2) cin>>b; else b=0; s1[i]=pi(a-1,b-1);}
for(int i=0; i<m; i++) {int c; cin>>c>>a; if(c==2) cin>>b; else b=0; s2[i]=pi(a-1,b-1);
sz1[i]=2-c, adj0[a-1].pb(i); if(c==2) adj0[b-1].pb(i);}
vector<int> rm; for(int i=0; i<m; i++) if(sz1[i]) rm.pb(i);
while(!rm.empty())
{
int i=rm.back(); rm.pop_back(); if(!edge[i]) continue; if(s2[i].f==-1) return -1;
ans+=s2[i].f==s1[i].f||s2[i].f==s1[i].s, edge[i]=0;
#ifdef DEBUG
cout<<"Assign "<<i<<' '<<s2[i].f<<" ("<<s2[i].s<<")\n";
#endif
for(int j:adj0[s2[i].f]) {if(s2[j].f==s2[i].f) s2[j].f=s2[j].s; s2[j].s=-1, rm.pb(j);}
}
int E=0;
for(int i=0; i<m; i++) if(edge[i]) adj[s2[i].f].pb(E),adj[s2[i].s].pb(E), e[E]=s2[i],es[E++]=s1[i];
m=E;
}
#ifdef DEBUG
cout<<m<<' '<<ans<<'\n'; for(int i=0; i<m; i++) cout<<e[i].f<<' '<<e[i].s<<' '<<es[i].f<<' '<<es[i].s<<'\n';
for(int i=0; i<n; i++) {cout<<i<<'\t'; for(int t:adj[i]) cout<<t<<' '; cout<<'\n';} cout<<flush;
#endif
for(int i=0; i<n; i++) if(!vis[i])
{
//count edges
cntE=cntV=0, dfs1(i); if(cntE>2*cntV) return -1;
if(cntE==2*cntV)
{
//cycle with trees: explicit formul
cycledone=0, dfs2(i,-1);
cnt3=0, dfs3(rt,-1), ans+=cnt3;
cv.clear(),cn.clear(), getcycle(rt);
int A=0,B=0,C=0; for(int j=0; j<cv.size(); j++)
{
bool x1=cn[j]==es[cv[j]].f||cn[j]==es[cv[j]].s, x2=cn[j+1]==es[cv[j]].f||cn[j+1]==es[cv[j]].s;
if(x1&&x2) C++; else if(x1) A++; else if(x2) B++;
}
ans+=min(min(A,B)+C,(A+B+C)/2);
}
else
{
//tree with variable root: dp
;
}
}
return ans;
}
int main()
{
int T; cin.sync_with_stdio(0),cin.tie(0),cin>>T; while(T--) cout<<solve()<<'\n';
}// #define DEBUG
int TIMER=20;
#define TIME if(!TIMER--)exit(0);
#pragma GCC optimize("O3")
#include <iostream>
#include <vector>
using namespace std; const int mx=1000000; typedef pair<int,int> pi;
#define pb push_back
#define f first
#define s second
pi s1[mx],s2[mx]; bool sz1[mx],edge[mx]; vector<int> adj0[mx]; //initial sets
pi e[mx],es[mx]; vector<int> adj[mx]; //graph
int cntE,cntV; bool vis[mx]; //components
void dfs1(int c)
{
if(vis[c]) return; cntV++, cntE+=adj[c].size(), vis[c]=1;
for(int t:adj[c]) dfs1(e[t].f+e[t].s-c);
}
bool cycle[mx],cycledone; int cpar[mx],rt;
void dfs2(int c, int p)
{
for(int t:adj[c]) if(t!=p)
{
int k=e[t].f+e[t].s-c; if(cpar[k]==-1) cpar[k]=c, dfs2(k,t); else if(!cycledone)
{rt=c,cycledone=1; while(1) {cycle[rt]=1; if(rt==k) break; rt=cpar[rt];}}
}
}
bool vis3[mx]; int cnt3;
void dfs3(int c, int p)
{
if(vis3[c]) return; vis3[c]=1; for(int t:adj[c])
{
int k=e[t].f+e[t].s-c; if(k==p) continue; dfs3(k,c);
if(!cycle[k]) cnt3+=k==es[t].f||k==es[t].s;
}
}
vector<int> cv, cn;
void getcycle(int c)
{
cn.pb(c); if(c==rt&&!cv.empty()) return; for(int t:adj[c])
{
int k=e[t].f+e[t].s-c;
if(cycle[k]&&!(!cv.empty()&&t==cv.back())) {cv.pb(t), getcycle(k); break;}
}
}
int solve()
{
#ifdef DEBUG
cout<<"Start\n"<<flush;
#endif
//note: swap m/n
int m,n,ans=0; cin>>m>>n;
for(int i=0; i<n; i++) adj0[i].clear(),adj[i].clear(), vis[i]=vis3[i]=0, cycle[i]=0, cpar[i]=-1;
for(int i=0; i<m; i++) edge[i]=1;
//input and delete |T|=1
{
int a,b;
for(int i=0; i<m; i++) {int c; cin>>c>>a; if(c==2) cin>>b; else b=0; s1[i]=pi(a-1,b-1);}
for(int i=0; i<m; i++) {int c; cin>>c>>a; if(c==2) cin>>b; else b=0; s2[i]=pi(a-1,b-1);
sz1[i]=2-c, adj0[a-1].pb(i); if(c==2) adj0[b-1].pb(i);}
vector<int> rm; for(int i=0; i<m; i++) if(sz1[i]) rm.pb(i);
while(!rm.empty())
{
int i=rm.back(); rm.pop_back(); if(!edge[i]) continue; if(s2[i].f==-1) return -1;
ans+=s2[i].f==s1[i].f||s2[i].f==s1[i].s, edge[i]=0;
#ifdef DEBUG
cout<<"Assign "<<i<<' '<<s2[i].f<<" ("<<s2[i].s<<")\n";
#endif
for(int j:adj0[s2[i].f]) {if(s2[j].f==s2[i].f) s2[j].f=s2[j].s; s2[j].s=-1, rm.pb(j);}
}
int E=0;
for(int i=0; i<m; i++) if(edge[i]) adj[s2[i].f].pb(E),adj[s2[i].s].pb(E), e[E]=s2[i],es[E++]=s1[i];
m=E;
}
#ifdef DEBUG
cout<<m<<' '<<ans<<'\n'; for(int i=0; i<m; i++) cout<<e[i].f<<' '<<e[i].s<<' '<<es[i].f<<' '<<es[i].s<<'\n';
for(int i=0; i<n; i++) {cout<<i<<'\t'; for(int t:adj[i]) cout<<t<<' '; cout<<'\n';} cout<<flush;
#endif
for(int i=0; i<n; i++) if(!vis[i])
{
//count edges
cntE=cntV=0, dfs1(i); if(cntE>2*cntV) return -1;
if(cntE==2*cntV)
{
//cycle with trees: explicit formul
cycledone=0, dfs2(i,-1);
cnt3=0, dfs3(rt,-1), ans+=cnt3;
cv.clear(),cn.clear(), getcycle(rt);
int A=0,B=0,C=0; for(int j=0; j<cv.size(); j++)
{
bool x1=cn[j]==es[cv[j]].f||cn[j]==es[cv[j]].s, x2=cn[j+1]==es[cv[j]].f||cn[j+1]==es[cv[j]].s;
if(x1&&x2) C++; else if(x1) A++; else if(x2) B++;
}
ans+=min(min(A,B)+C,(A+B+C)/2);
}
else
{
//tree with variable root: dp
;
}
}
return ans;
}
int main()
{
int T; cin.sync_with_stdio(0),cin.tie(0),cin>>T; while(T--) cout<<solve()<<'\n';
}