Problem:
B: Brilliant Stars

ICPC で良い成績を収めるには修行が欠かせない．うさぎは ICPC で勝ちたいので，今日も修行をすることにした．

今日の修行は，夜空に輝くたくさんの星たちを観測して，視力と集中力を鍛えようというものである．うさぎは独自のセンスで星の特徴を記録していくことができる．

うさぎは「星の観測日記」として記録した星の特徴をノートにつけていくことにした．ここで，「似ている」 2 つの星をともに記録することは避けたい．2 つの星が「似ている」かどうかは，すべてうさぎの判断で決まっている．「似ている」かどうかの関係はあまり複雑ではなく，以下の条件を満たす．

条件： 異なる 4 つの星 A, B, C, D があり，A と B，B と C，C と D がそれぞれ「似ている」とする．このとき，A と C が「似ている」か，B と D が「似ている」か，あるいはその両方が成り立つ．

うさぎは「似ている」 2 つの星を記録しないようになるべくたくさんの星を記録したい．また，そのようにする方法が何通りあるかも知りたい．

Input

N M
X1 Y1
...
XM YM

N は星の個数，M は「似ている」 2 つの星の組の個数である．星は 1 以上 N 以下の整数の番号により表され，Xi, Yi (1 ≤ i ≤ M) は星 Xi と星 Yi が「似ている」ことを表す．

1 ≤ N ≤ 100,000，0 ≤ M ≤ 200,000，1 ≤ Xi < Yi ≤ N を満たす．(Xi, Yi) として同一の組は複数回現れない．「似ている」かどうかの関係は，問題文で指定された条件を満たす．

Output

1 行目に，「似ている」 2 つの星を記録しないように記録できる星の個数の最大値を，2 行目に，その最大値を実現できる星の組み合わせの個数 (記録する順番のみを変えたものは同じと考える) を 1,000,000,009 で割った余りを出力せよ．

Sample Input 1

4 3
1 2
1 3
1 4

Sample Output 1

3
1

Sample Input 2

11 5
1 2
3 4
5 6
7 8
9 10

Sample Output 2

6
32

Sample Input 3

9 14
1 2
1 3
2 3
3 4
3 5
3 6
3 7
3 8
3 9
4 5
4 6
5 6
7 8
8 9

Sample Output 3

4
6

Solution in C++14 (judge verdict: Accepted)

#include <bits/stdc++.h>
using namespace std;

class UnionFind {
private:
	vector< int > data;
	
public:
	UnionFind(int n) : data(n, -1) {}
	
	void unionSet(int x, int y) {
		x = root(x); y = root(y);
		if (data[y] < data[x]) swap(x, y);
		if (x != y) { data[x] += data[y]; data[y] = x; }
	}
	
	bool sameSet(int x, int y) { return root(x) == root(y); }
	int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
	int size(int x) { return -data[root(x)]; }
};

int N, M;
vector< vector< int > > aList;

vector< int > getDegOrder() {
	vector< int > deg(N, 0);
	for (int v=0; v<N; ++v) deg[v] = (int)aList[v].size();
	
	using pii = pair< int, int >;
	set< pii > que;
	for (int v=0; v<N; ++v) que.insert(pii(-deg[v], v));
	
	vector< int > ret;
	while (!que.empty()) {
		int v = que.begin()->second; que.erase(que.begin());	
		if (deg[v] < 0) continue;
		ret.push_back(v); deg[v] = -1;
		for (int u : aList[v]) {
			if (deg[u] < 0) continue;
			que.erase(pii(-deg[u], u));	--deg[u];
			que.insert(pii(-deg[u], u));
		}
	}
	return ret;
}

vector< int > constructCliques(const vector< int >& vertex_order) {
	vector< int > mark(N, 0);
	
	UnionFind uf(N);
	for (int v : vertex_order) {
		mark[v] = 1;
		int g = -1;
		for (int u : aList[v]) {
			if (mark[u] == 0) continue;
			int r = uf.root(u);
			if (g != r) g = (g == -1 ? r : -2);
		}
		if (g >= 0) uf.unionSet(g, v);
		if (g == -2) mark[v] = -1;
	}
	
	vector< int > ret;
	for (int v=0; v<N; ++v) {
		if (mark[uf.root(v)] == -1) continue;
		mark[uf.root(v)] = -1;
		ret.push_back(uf.size(v));
	}
	return ret;
}

int main() {
	cin >> N >> M;
	aList.assign(N, vector< int >());
	for (int i=0; i<M; ++i) {
		int u, v; cin >> u >> v; --u; --v;
		aList[u].push_back(v);
		aList[v].push_back(u);
	}
	
	vector< int > deg_order = getDegOrder();
	reverse(deg_order.begin(), deg_order.end());
	vector< int > cliqueSize = constructCliques(deg_order);
	
	using lint = long long int;
	lint ans = 1, MOD = (lint)1e9 + 9;
	for (int x : cliqueSize) ans = (ans * x) % MOD;
	cout << cliqueSize.size() << endl << ans << endl;
}