1. 程式人生 > 其它 >P4092 [HEOI2016/TJOI2016]樹

P4092 [HEOI2016/TJOI2016]樹

題面

在 2016 年,佳媛姐姐剛剛學習了樹,非常開心。現在他想解決這樣一個問題:給定一顆有根樹,根為 \(1\) ,有以下兩種操作:

  • 標記操作:對某個結點打上標記。(在最開始,只有結點 \(1\) 有標記,其他結點均無標記,而且對於某個結點,可以打多次標記。)

  • 詢問操作:詢問某個結點最近的一個打了標記的祖先。(這個結點本身也算自己的祖先)

你能幫幫她嗎?

思路

1.暴力圖遍歷

可以考慮建反圖,然後標記用一個數組維護,詢問則以該節點為源點,像下(原圖為上)跑DFS遍歷,直到 遍歷到被標記的節點即可。

由於是樹,所以不會出現死迴圈的情況。

最壞時間複雜度為 \(O(nq)\)

#include <bits/stdc++.h>
#define SIZE ((int(1e5))+5)
using namespace std;

int n, q, ec, juli = INT_MAX, ans;

int head[SIZE];
int tag[SIZE];
struct edge {
	int next, to;
} edges[SIZE];

void add(int from, int to) {
	edges[++ec].next = head[from];
	edges[ec].to = to;
	head[from] = ec;
}

void dfs(int u, int ndeep) {
	if (tag[u] > 0 && juli > ndeep) {
		ans = u;
		juli = ndeep;
	}
	for (int j = head[u]; j; j = edges[j].next) {
		int v = edges[j].to;
		dfs(v, ndeep + 1);
	}
}

int main() {
	cin >> n >> q;
	for (int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		add(v, u);
	}
	tag[1] = 1;
	while (q--) {
		char op;
		int u;
		cin >> op >> u;
		if (op == 'C') {
			tag[u] = 1;
		} else {
			dfs(u, 0);
			cout << ans << endl;
			juli = INT_MAX;
		}
	}
}

能水68分。