競プロ・数学を頑張りたい(願望)

競技プログラミングの問題を解いたときや数学に関してのメモにしようと思っています。競プロはAOJを、数学は数検準1を目標で。

ABC#19 D問題「高橋くんと木の直径」

コメント

全然分からなかった\(^o^)/
…これでもグラフ理論系の研究室に入っているんだけど(汗ダラダラ

このリアクティブ形式(解説見て初めて知った)という問題が初めてだったのでそれにやられたのもある。
ただ、解説を見たらやることは理解した。しかし、double-sweepという方法は直感的にはなんとなく理解したけど、証明がいまいち理解していない。もう少し考えてみる。

コード

※solve100メソッドは解説のコードを見ながら書いたもの。

import java.util.Scanner;

public class Main {

	Scanner sc = new Scanner(System.in);

	void run() {
		int n = sc.nextInt();

		// solve20(n);
		solve100(n);

		sc.close();
	}

	// ある頂点a,bの距離をすべての頂点に対して入力して、その最大値が木の直径。
	// ⇒最大質問回数N*(N-1)/2 = (50*49)/2 = 1225 <= 1300
	void solve20(int n) {
		int max = 0;

		for (int i = 1; i <= n; i++) {
			for (int j = i + 1; j <= n; j++) {
				System.out.printf("? %d %d\n", i, j);
				int x = sc.nextInt();
				max = Math.max(max, x);
			}
		}

		System.out.printf("! %d\n", max);

	}

	// 木の直径はある頂点aから一番遠い頂点bを選び、
	// さらに頂点bから一番遠い頂点cを選んだときの長さになる。
	void solve100(int n) {
		int best = 0;
		int bestdist = 0;

		// 頂点aから一番遠い頂点bを選択(N-1回)
		for (int i = 2; i <= n; i++) {
			int dist = in(1, i);
			if (bestdist < dist) {
				bestdist = dist;
				best = i;
			}
		}

		// 頂点bから一番遠い頂点cを選択(N-1回)
		int ret = 0;
		for (int i = 1; i <= n; i++) {
			if (best == i)
				continue;
			ret = Math.max(ret, in(best, i));
		}
		System.out.printf("! %d\n", ret);
	}

	int in(int a, int b) {
		System.out.printf("? %d %d\n", a, b);
		return sc.nextInt();
	}

	public static void main(String[] args) {
		new Main().run();
	}

}