#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#define ull unsigned long long
using namespace std;
ull max(ull a,ull b){
return a>b?a:b;
}
struct Edge{
int next;
int v;
}edge[1000005];
int cnt=1,head[100005];
void addedge(int u,int v){
edge[cnt].next=head[u];
edge[cnt].v=v;
head[u]=cnt++;
}
struct frac{
ull cnt2=0,cnt3=0,cnt5=0;
long double son=0;
frac operator +(frac b){
frac c;
c.cnt2=max(cnt2,b.cnt2);
c.cnt3=max(cnt3,b.cnt3);
c.cnt5=max(cnt2,b.cnt5);
c.son=0;
long double t1=son*pow(2,c.cnt2-cnt2)*pow(3,c.cnt3-cnt3)*pow(5,c.cnt5-cnt5);
long double t2=b.son*pow(2,c.cnt2-b.cnt2)*pow(3,c.cnt3-b.cnt3)*pow(5,c.cnt5-b.cnt5);
t1+=t2;
while(round(t1/2)*2==t1&&c.cnt2>0)--c.cnt2,t1/=2;
while(round(t1/3)*3==t1&&c.cnt3>0)--c.cnt3,t1/=3;
while(round(t1/5)*5==t1&&c.cnt5>0)--c.cnt5,t1/=5;
c.son=t1;
return c;
}
};
frac w[100005];
int n,m;
int out[100005],in[100005];
void toposort(){
queue<int>q;
for(int i=1;i<=n;i++)if(in[i]==0){q.push(i);w[i].son=1;}
while(!q.empty()){
int front=q.front();
q.pop();
frac t=w[front];
if(out[front]==0)continue;
if(out[front]==2)t.cnt2++;
if(out[front]==3)t.cnt3++;
if(out[front]==4)t.cnt2+=2;
if(out[front]==5)t.cnt5++;
for(int i=head[front];i;i=edge[i].next){
int x=edge[i].v;
q.push(x);
w[x]=w[x]+t;
}
w[front]=frac();
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",out+i);
for(int j=1;j<=out[i];j++){
int x;
scanf("%d",&x);
addedge(i,x);
in[x]++;
}
}
toposort();
for(int i=1;i<=n;i++)
if(out[i]==0){
frac a=w[i];
printf("%.0Lf %.0Lf\n",a.son,((long double)pow(2,a.cnt2)*pow(3,a.cnt3)*pow(5,a.cnt5)));
}
return 0;
}