結果

問題 No.1293 2種類の道路
ユーザー ks2mks2m
提出日時 2020-11-20 22:59:40
言語 Java
(openjdk 23)
結果
AC  
実行時間 983 ms / 2,000 ms
コード長 4,697 bytes
コンパイル時間 2,933 ms
コンパイル使用メモリ 90,840 KB
実行使用メモリ 94,176 KB
最終ジャッジ日時 2024-07-23 13:38:16
合計ジャッジ時間 15,896 ms
ジャッジサーバーID
(参考情報)
judge3 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 2
other AC * 22
権限があれば一括ダウンロードができます

ソースコード

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