module main; // https://mayokoex.hatenablog.com/entry/2015/12/11/162941 より // 連結成分、動的計画法 import std; // atcoder-library より // 無向グラフに対して辺の追加と、頂点a,b間が連結かどうかをO(α(n))で求める struct Dsu { private: int n; int[] parentOrSize; public: this(int n) { this.n = n; parentOrSize = [-1].replicate(n); } int merge(int a, int b) { assert(0 <= a && a < n); assert(0 <= b && b < n); int x = leader(a), y = leader(b); if (x == y) return x; if (-parentOrSize[x] < -parentOrSize[y]) swap(x, y); parentOrSize[x] += parentOrSize[y]; parentOrSize[y] = x; return x; } bool same(int a, int b) { assert(0 <= a && a < n); assert(0 <= b && b < n); return leader(a) == leader(b); } int leader(int a) { assert(0 <= a && a < n); if (parentOrSize[a] < 0) return a; return parentOrSize[a] = leader(parentOrSize[a]); } int size(int a) { assert(0 <= a && a < n); return -parentOrSize[leader(a)]; } int[][] groups() { auto leaderBuf = iota(0, n).map!(i => leader(i)).array; auto groupSize = new int[](n); foreach (i; 0 .. n) groupSize[leaderBuf[i]]++; auto result = new int[][](n); foreach (i; 0 .. n) result[leaderBuf[i]] ~= i; result = result.remove!(a => a.empty); return result; } } void main() { // 入力 int N, M; readln.chomp.formattedRead("%d %d", N, M); auto dsu = Dsu(N); foreach (_; 0 .. M) { int u, v; readln.chomp.formattedRead("%d %d", u, v); u--, v--; if (!dsu.same(u, v)) { dsu.merge(u, v); } } // 答えの計算 auto cnt = new int[](N + 1); foreach (g; dsu.groups) cnt[g.length]++; immutable INF = 10 ^^ 9; auto ans = uninitializedArray!(int[])(N + 1); ans[] = INF; ans[0] = 0; foreach (i; 1 .. N + 1) { for (int k = 1; cnt[i]; k <<= 1) { int num = min(cnt[i], k); foreach_reverse (j; 0 .. N - num * i + 1) { int nj = j + num * i; ans[nj] = min(ans[nj], ans[j] + num); } cnt[i] -= num; } } // 答えの出力 foreach (i; 1 .. N + 1) writeln(ans[i] == INF ? -1 : ans[i] - 1); }