#   Author: yumtam
#   Created at: 2020-12-31 01:27

from __future__ import division, print_function
_interactive = False

def main():
    n, m = input_as_list()
    A = [input_with_offset(-1)[1:] for _ in range(n)]

    uf = DisjointSetUnion(m)
    dims = 0
    for e in A:
        if len(e) == 1:
            x = e[0]
            if not uf.is_done(x):
                uf.set_done(x)
                dims += 1
        elif len(e) == 2:
            x, y = e
            if uf.find(x) != uf.find(y):
                dx, dy = uf.is_done(x), uf.is_done(y)
                both = dx and dy
                done = dx or dy
                uf.union(x, y)
                if not both:
                    dims += 1
                if done:
                    uf.set_done(x)

    print(pow(2, dims, MOD), dims)

    uf = DisjointSetUnion(m)
    dims_orig = dims
    dims = 0
    ans = []
    for i, e in enumerate(A, start=1):
        if len(e) == 1:
            x = e[0]
            if not uf.is_done(x):
                uf.set_done(x)
                dims += 1
                ans += [i]
        elif len(e) == 2:
            x, y = e
            if uf.find(x) != uf.find(y):
                dx, dy = uf.is_done(x), uf.is_done(y)
                both = dx and dy
                done = dx or dy
                uf.union(x, y)
                if not both:
                    dims += 1
                    ans += [i]
                if done:
                    uf.set_done(x)
        if dims == dims_orig:
            break
    print(*ans, sep=' ')


# Constants
INF = float('inf')
MOD = 10**9+7

# Python3 equivalent names
import os, sys, itertools
if sys.version_info[0] < 3:
    input = raw_input
    range = xrange

    filter = itertools.ifilter
    map = itertools.imap
    zip = itertools.izip

# print-flush in interactive problems
if _interactive:
    flush = sys.stdout.flush
    def printf(*args, **kwargs):
        print(*args, **kwargs)
        flush()

# Debug print, only works on local machine
LOCAL = "LOCAL_" in os.environ
debug_print = (print) if LOCAL else (lambda *x, **y: None)

# Fast IO
if (not LOCAL) and (not _interactive):
    from io import BytesIO
    from atexit import register
    sys.stdin = BytesIO(os.read(0, os.fstat(0).st_size))
    sys.stdout = BytesIO()
    register(lambda: os.write(1, sys.stdout.getvalue()))
    input = lambda: sys.stdin.readline().rstrip('\r\n')

# Some utility functions(Input, N-dimensional lists, ...)
def input_as_list():
    return [int(x) for x in input().split()]

def input_with_offset(o):
    return [int(x)+o for x in input().split()]

def input_as_matrix(n, m):
    return [input_as_list() for _ in range(n)]

def array_of(f, *dim):
    return [array_of(f, *dim[1:]) for _ in range(dim[0])] if dim else f()

# Start of external code templates...
class DisjointSetUnion:
    def __init__(self, n):
        self.parent = list(range(n))
        self.size = [1] * n
        self.num_sets = n
        self.done = [0] * n

    def is_done(self, a):
        return self.done[self.find(a)]

    def set_done(self, a):
        self.done[self.find(a)] = 1

    def find(self, a):
        acopy = a
        while a != self.parent[a]:
            a = self.parent[a]
        while acopy != a:
            self.parent[acopy], acopy = a, self.parent[acopy]
        return a

    def union(self, a, b):
        a, b = self.find(a), self.find(b)
        if a != b:
            if self.size[a] < self.size[b]:
                a, b = b, a

            self.num_sets -= 1
            self.parent[b] = a
            self.size[a] += self.size[b]

    def set_size(self, a):
        return self.size[self.find(a)]

    def __len__(self):
        return self.num_sets

# End of external code templates.

main()
