結果

問題 No.317 辺の追加
コンテスト
ユーザー ゴリポン先生
提出日時 2026-10-12 15:31:11
言語 D
(dmd 2.113.0)
コンパイル:
dmd -fPIE -m64 -w -wi -O -release -inline -I/opt/dmd/src/druntime/import/ -I/opt/dmd/src/phobos -L-L/opt/dmd/linux/lib64/ -fPIC _filename_
実行:
./Main
結果
WJ  
(最新)
AC  
(最初)
実行時間 -
コード長 2,078 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
最終ジャッジ日時 2026-10-12 15:31:28
ジャッジサーバーID
(参考情報)
judge4_1 / judge2_0
このコードへのチャレンジ
(要ログイン)

ソースコード

diff #
raw source code

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);
}
0