Problem:
C: な◯りカット (Namo.. Cut)

問題

－謎の巨大クラゲ、 コードネーム「な◯り」を討伐せよ－

「な◯り」は非常に生命力が強いため、素早く切断し続けなければ、あっという間に復活してしまう。
我々は、「な◯り」をどのように切断するのが効率良いのか、日々試行錯誤している。
その過程で、あなた方プログラマの手が必要になった。

「な◯り」は N 頂点と N 辺からなる連結な無向グラフで表現できる。
以降、各頂点が 1 から N の異なる数で名前付けられているとしよう。

我々は、「な◯り」に関して Q 回の質問を行う。
それらすべてに答えるプログラムを作成して欲しい。

質問は 1 から Q の番号を持ち、各質問は次のように構成される。

質問 i では 2 つの頂点 a_i と b_i が指定される。 a_i と b_i を非連結にするために、削除する必要のある辺の最小本数を答えよ。

ここで、頂点 u と v が非連結であるとは、 u と v を行き来できる経路が存在しないことを指す。

入力形式

N
u_1 v_1
u_2 v_2
...
u_N v_N
Q
a_1 b_1
a_2 b_2
...
a_Q b_Q

入力はすべて整数である。

1 行目には頂点数 N が与えられる。
続く N 行のうち i 行目には、i 番目の辺が繋ぐ 2 頂点の番号 u_i, v_i が空白区切りで与えられる。

次に、質問の回数 Q が与えられる。
続く Q 行のうち i 行目には、i 番目の質問で指定される 2 頂点の番号 a_i, b_i が空白区切りで与えられる。

制約

3 \leq N \leq 100,000

1 \leq Q \leq 100,000

グラフに自己ループ及び多重辺は存在しない

1 \leq a_i, b_i \leq N かつ a_i \neq b_i (1 \leq i \leq Q)

出力形式

出力は Q 行からなる。
i 行目には、a_i と b_i を非連結にするために、削除する必要のある辺の最小本数を表す整数を出力せよ。

入力例1

3
1 2
1 3
2 3
1
1 3

出力例1

2

入力例2

7
1 2
1 6
3 5
2 5
5 4
1 4
3 7
3
2 4
3 1
6 7

出力例2

2
1
1

Solution in C (judge verdict: Accepted)

// AOJ 2891 Namo.. Cut
// 2019.3.22 bal4u

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//// 高速数値入力
#if 1
#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)
#else
#define gc() getchar()
#define pc(c) putchar(c)
#endif
int in()
{
	int n = 0, c = gc();
	do n = 10 * n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

//// 閉路の検出
#define MAX_V 100002
int  *to[MAX_V], hi[MAX_V];
char cycle[MAX_V];            // 検出の結果

void dfs(int V)
{
	int i, j, f, node, par, next;
	static char used[MAX_V];
	static int path[MAX_V], sz;
	typedef struct { int node, par; } Q;
	static Q q[MAX_V*2]; int top;

	f = 0, sz = 0, top = 1;
	q[0].node = 1, q[0].par = 0;
	while (top) {
		node = q[--top].node, par = q[top].par;
		//		printf("top=%d, node=%d, par=%d, sz=%d\n", top, node, par, sz);
		if (node < 0) { sz--; continue; }
		used[node] = 1;
		path[sz++] = node;
		q[top++].node = -1;

		for (i = 0; i < hi[node]; i++) {
			next = to[node][i];
			if (next == par) continue;
			if (used[next] && !f) {
				f = 1;
				for (j = sz - 1; path[j] != next; j--) cycle[path[j]] = 1;
				cycle[path[j]] = 1;
			}
			else if (!used[next]) q[top].node = next, q[top++].par = node;
		}
	}
}

int main()
{
	int i, N, Q, u, v;
	int *memo, sz;

	// 入力受付。C言語ではややこしい
	N = in();
	memo = malloc(sizeof(int) * N * 2);
	sz = 0; for (i = 0; i < N; i++) {
		memo[sz++] = u = in(), memo[sz++] = v = in();
		hi[u]++, hi[v]++;
	}
	for (i = 1; i <= N; i++) {
		if (hi[i]) to[i] = malloc(sizeof(int) * hi[i]);
	}
	memset(hi, 0, sizeof(int) * (N + 1));
	i = 0; while (i < sz) {
		u = memo[i++], v = memo[i++];
		to[u][hi[u]++] = v, to[v][hi[v]++] = u;
	}

	dfs(N + 1);     // 閉路の検出 閉路上にある頂点 u --> cycle[u] == 1

	Q = in();
	while (Q--) {
		u = in(), v = in();
		pc((cycle[u] & cycle[v]) ? '2' : '1'), pc('\n');
	}
	return 0;
}