#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;

#define all(x) begin(x), end(x)
#define sz(x) ((int) (x).size())
#define x first
#define y second
typedef long long ll;
typedef long double ld;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

template<typename T>
using normal_queue = priority_queue<T, vector<T>, greater<T>>;

const int N = 5e5 + 10, MOD = 1e9 + 7;

struct DSU {
	int p[N], r[N], mark[N], siz[N];

	DSU() {
		iota(all(p), 0);
		fill(all(r), 0);
		fill(all(siz), 1);
	}

	int get(int u) {
		if (p[u] == u)
			return u;
		return p[u] = get(p[u]);
	}

	bool merge(int a, int b) {
		a = get(a);
		b = get(b);
		if (a == b)
			return false;
		if (r[a] == r[b])
			++r[a];
		if (r[a] < r[b])
			swap(a, b);
		p[b] = a;
		siz[a] += siz[b];
		mark[a] |= mark[b];
		return true;
	}
} dsu;

ll pw2[N];

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	pw2[0] = 1;
	for (int i = 1; i < N; ++i)
		pw2[i] = pw2[i - 1] * 2 % MOD;
	int n, m;
	cin >> n >> m;
	vector<int> ans;
	for (int i = 0; i < n; ++i) {
		int k, u, v;
		cin >> k >> u;
		--u;
		if (k == 1) {
			int x = dsu.get(u);
			if (!dsu.mark[x]) {
				dsu.mark[x] = 1;
				ans.push_back(i + 1);
			}
		} else {
			cin >> v;
			--v;
			int x = dsu.get(u), y = dsu.get(v);
			if (!dsu.mark[x] || !dsu.mark[y]) {
				dsu.merge(x, y);
				ans.push_back(i + 1);
			}
		}
	}
	ll tsz = 1;
	for (int i = 0; i < m; ++i) {
		if (dsu.p[i] == i) {
			// cout << i << " sz is " << dsu.siz[i] << ", mark " << dsu.mark[i] << endl;
			(tsz *= pw2[dsu.siz[i] - 1 + dsu.mark[i]]) %= MOD;
		}
	}
	cout << tsz << " " << sz(ans) << "\n";
	for (int u : ans)
		cout << u << " ";
	return 0;
}