from collections import defaultdict
MOD=10**9+7
import sys
input=sys.stdin.buffer.readline #FOR READING PURE INTEGER INPUTS (space separation ok)
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
    def find(self, a): #return parent of a. a and b are in same set if they have same parent
        acopy = a
        while a != self.parent[a]:
            a = self.parent[a]
        while acopy != a: #path compression
            self.parent[acopy], acopy = a, self.parent[acopy]
        return a
    def union(self, a, b): #union a and b
        self.parent[self.find(b)] = self.find(a)

def oneLineArrayPrint(arr):
    print(' '.join([str(x+1) for x in arr]))
def solveActual():
    uf=UnionFind(m)
    
    #each component has k vertices.
    #each component shall have at most k-1 useful edges if componentHasOne==False, or k edges if it's True
    #pick the k-1 or k edges with the smallest values
    #each component contributes independently 2**(nEdges) to the number of ways.
    #just find 2**(nEdges or number of included indexes)
    
    sPrime=[]
    cEc=[0 for _ in range(m)] #cEc[parent]=component edge counts
    cVc=[0 for _ in range(m)] #cVc[parent]=component vertex count
    cHO=[False for _ in range(m)] #cHO[parent]=True if this component has an edge with only 1 vertex
    vV=[False for _ in range(m)] #True if the vertex has been visited
    for i,x in enumerate(vS): #idx,vertex(vertices)
#        print('edge:{} cVc:{}'.format(i,cVc))
        if len(x)==2:
            v1,v2=x
            p1,p2=uf.find(v1),uf.find(v2)
            ec1,ec2=cEc[p1],cEc[p2]
            vc1,vc2=cVc[p1],cVc[p2]
            hO1,hO2=cHO[p1],cHO[p2]
            if p1!=p2: #not yet joined. definitely can take
                oldEdgeCnt=ec1+ec2
                oldVertexCnt=vc1+vc2
                newEdgeCnt=oldEdgeCnt+1
                newVertexCnt=oldVertexCnt
                if vV[v1]==False:
                    newVertexCnt+=1
                if vV[v2]==False:
                    newVertexCnt+=1
                canTake=False
                if hO1 or hO2:
                    if newVertexCnt>=newEdgeCnt:
                        canTake=True
                else:
                    if newVertexCnt>newEdgeCnt:
                        canTake=True
#                print('p:{} oldEC:{} oldVC:{} newEC:{} newVC:{} canTake:{}'.format(p1,oldEdgeCnt,oldVertexCnt,newEdgeCnt,newVertexCnt,canTake))
                if canTake:
                    uf.union(p1,p2)
                    pNew=uf.find(v1)
                    sPrime.append(i)
                    
                    cVc[pNew]=newVertexCnt
                    cEc[pNew]=newEdgeCnt
                    cHO[pNew]=hO1 or hO2
                    vV[v1]=True
                    vV[v2]=True
            else: #check if can take
                oldEdgeCnt=ec1+ec2
                oldVertexCnt=vc1+vc2
                newEdgeCnt=oldEdgeCnt+1
                newVertexCnt=oldVertexCnt
#                print('p:{} oldEC:{} oldVC:{} newEC:{} newVC:{}'.format(p1,oldEdgeCnt,oldVertexCnt,newEdgeCnt,newVertexCnt))
                canTake=False
                if hO1 or hO2:
                    if newVertexCnt>=newEdgeCnt:
                        canTake=True
                else:
                    if newVertexCnt>newEdgeCnt:
                        canTake=True
                if canTake: #can add
                    uf.union(p1,p2)
                    pNew=uf.find(v1)
                    sPrime.append(i)
                    cVc[pNew]=newVertexCnt
                    cEc[pNew]=newEdgeCnt
                    cHO[pNew]=hO1 or hO2
#                    vV[v1]=True unnecessary because guaranteed to be visited
#                    vV[v2]=True
                else:
                    pass #don't take
        else: #single vertex
            v=x[0]
            p=uf.find(v)
            ec=cEc[p]
            vc=cVc[p]
            
            oldEdgeCnt=ec
            oldVertexCnt=vc
            newEdgeCnt=ec+1
            newVertexCnt=oldVertexCnt
            if vV[v]==False:
                newVertexCnt+=1
            canTake=False
            if newVertexCnt>=newEdgeCnt:
                canTake=True
            if canTake:
                sPrime.append(i)
                cVc[p]=newVertexCnt
                vV[v]=True
                cEc[p]=newEdgeCnt
                cHO[p]=True
    
    
    TSize=pow(2,len(sPrime),MOD)
    print('{} {}'.format(TSize,len(sPrime)))
    oneLineArrayPrint(sPrime)


n,m=[int(x) for x in input().split()]
vS=[] #0-indexed

for _ in range(n):
    xx=[int(x) for x in input().split()]
    #number of 1s, coordinates with 1s
    for i in range(1,len(xx)):
        xx[i]-=1 #0-index
    vS.append(xx[1:])
solveActual()