結果

問題 No.1293 2種類の道路
ユーザー ks2mks2m
提出日時 2020-11-20 22:59:40
言語 Java21
(openjdk 21)
結果
AC  
実行時間 947 ms / 2,000 ms
コード長 4,697 bytes
コンパイル時間 4,582 ms
コンパイル使用メモリ 94,712 KB
実行使用メモリ 96,624 KB
最終ジャッジ日時 2023-09-30 19:50:18
合計ジャッジ時間 17,463 ms
ジャッジサーバーID
(参考情報)
judge13 / judge15
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 54 ms
50,116 KB
testcase_01 AC 56 ms
50,420 KB
testcase_02 AC 54 ms
49,876 KB
testcase_03 AC 54 ms
50,100 KB
testcase_04 AC 58 ms
50,488 KB
testcase_05 AC 54 ms
49,920 KB
testcase_06 AC 53 ms
48,432 KB
testcase_07 AC 55 ms
50,116 KB
testcase_08 AC 62 ms
50,164 KB
testcase_09 AC 946 ms
83,724 KB
testcase_10 AC 878 ms
83,800 KB
testcase_11 AC 897 ms
81,876 KB
testcase_12 AC 904 ms
83,852 KB
testcase_13 AC 947 ms
83,188 KB
testcase_14 AC 761 ms
96,604 KB
testcase_15 AC 769 ms
96,624 KB
testcase_16 AC 838 ms
88,540 KB
testcase_17 AC 828 ms
87,552 KB
testcase_18 AC 680 ms
84,540 KB
testcase_19 AC 656 ms
82,716 KB
testcase_20 AC 653 ms
81,064 KB
testcase_21 AC 438 ms
58,636 KB
testcase_22 AC 434 ms
58,200 KB
testcase_23 AC 446 ms
58,572 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Main {
	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String[] sa = br.readLine().split(" ");
		int n = Integer.parseInt(sa[0]);
		int p = Integer.parseInt(sa[1]);
		int q = Integer.parseInt(sa[2]);
		DSU uf1 = new DSU(n);
		Set<Integer> set1 = new HashSet<>();
		for (int i = 0; i < p; i++) {
			sa = br.readLine().split(" ");
			int a = Integer.parseInt(sa[0]) - 1;
			int b = Integer.parseInt(sa[1]) - 1;
			uf1.merge(a, b);
			set1.add(a);
			set1.add(b);
		}
		DSU uf2 = new DSU(n);
		Set<Integer> set2 = new HashSet<>();
		for (int i = 0; i < q; i++) {
			sa = br.readLine().split(" ");
			int a = Integer.parseInt(sa[0]) - 1;
			int b = Integer.parseInt(sa[1]) - 1;
			uf2.merge(a, b);
			set2.add(a);
			set2.add(b);
		}
		br.close();

		List<List<Integer>> g1 = uf1.groups();
		List<List<Integer>> g2 = uf2.groups();
		Map<Integer, Set<Integer>> g22 = new HashMap<>();
		for (int i = 0; i < g2.size(); i++) {
			List<Integer> list = g2.get(i);
			Set<Integer> set = new HashSet<>();
			int l = uf2.leader(list.get(0));
			for (Integer o : list) {
				if (!set1.contains(o)) {
					set.add(o);
				}
			}
			g22.put(l, set);
		}
		long ans = 0;
		for (int i = 0; i < g1.size(); i++) {
			List<Integer> list = g1.get(i);
			if (!set1.contains(list.get(0))) {
				continue;
			}
			Set<Integer> set = new HashSet<>();
			for (int o : list) {
				int l = uf2.leader(o);
				if (set2.contains(l)) {
					set.add(l);
				}
			}
			long cnt = 0;
			for (int o : set) {
				cnt += uf2.size(o);
			}
			for (int o : list) {
				if (set.contains(uf2.leader(o))) {
					cnt--;
				}
			}
			ans += (long) list.size() * (list.size() - 1 + cnt);
		}

		for (Integer key : g22.keySet()) {
			long val1 = g22.get(key).size();
			long val2 = uf2.size(key) - 1;
			ans += val1 * val2;
		}
		System.out.println(ans);
	}
}

/**
 * Disjoint Set Union(Union Find)
 */
class DSU {
	private int n;
	private int[] parentOrSize;
	private int num;

	/**
	 * n頂点0辺の無向グラフを作る。<br>
	 * O(n)
	 * 
	 * @param n 頂点数
	 */
	public DSU(int n) {
		this.n = n;
		this.parentOrSize = new int[n];
		Arrays.fill(parentOrSize, -1);
		num = n;
	}

	/**
	 * 辺(a, b)を足す。<br>
	 * ならしO(α(n))
	 * 
	 * @param a 頂点番号(0≦a<n)
	 * @param b 頂点番号(0≦b<n)
	 * @return 代表元
	 */
	int merge(int a, int b) {
		assert 0 <= a && a < n : "a=" + a;
		assert 0 <= b && b < n : "b=" + b;

		int x = leader(a);
		int y = leader(b);
		if (x == y) {
			return x;
		}
		if (-parentOrSize[x] < -parentOrSize[y]) {
			int tmp = x;
			x = y;
			y = tmp;
		}
		parentOrSize[x] += parentOrSize[y];
		parentOrSize[y] = x;
		num--;
		return x;
	}

	/**
	 * 頂点a, bが連結かどうか。<br>
	 * ならしO(α(n))
	 * 
	 * @param a 頂点番号(0≦a<n)
	 * @param b 頂点番号(0≦b<n)
	 * @return true:連結 false:非連結
	 */
	boolean same(int a, int b) {
		assert 0 <= a && a < n : "a=" + a;
		assert 0 <= b && b < n : "b=" + b;

		return leader(a) == leader(b);
	}

	/**
	 * 頂点aの属する連結成分の代表元を返す。<br>
	 * ならしO(α(n))
	 * 
	 * @param a 頂点番号(0≦a<n)
	 * @return 代表元
	 */
	int leader(int a) {
		assert 0 <= a && a < n : "a=" + a;

		if (parentOrSize[a] < 0) {
			return a;
		} else {
			return parentOrSize[a] = leader(parentOrSize[a]);
		}
	}

	/**
	 * 頂点aの属する連結成分の要素数を返す。<br>
	 * ならしO(α(n))
	 * 
	 * @param a 頂点番号(0≦a<n)
	 * @return 要素数
	 */
	int size(int a) {
		assert 0 <= a && a < n : "a=" + a;

		return -parentOrSize[leader(a)];
	}

	/**
	 * 連結成分の数を返す。<br>
	 * O(1)
	 * 
	 * @return 連結成分数
	 */
	int num() {
		return num;
	}

	/**
	 * グラフを連結成分に分けた情報を返す。<br>
	 * O(n)
	 * 
	 * @return 「1つの連結成分の頂点番号のリスト」のリスト
	 */
	List<List<Integer>> groups() {
		int[] leaderBuf = new int[n];
		int[] groupSize = new int[n];
		for (int i = 0; i < n; i++) {
			leaderBuf[i] = leader(i);
			groupSize[leaderBuf[i]]++;
		}
		List<List<Integer>> result = new ArrayList<>(n);
		for (int i = 0; i < n; i++) {
			result.add(new ArrayList<>(groupSize[i]));
		}
		for (int i = 0; i < n; i++) {
			result.get(leaderBuf[i]).add(i);
		}
		result.removeIf(List::isEmpty);
		return result;
	}
}
0