1. 程式人生 > >[NOIP TG D2T1]旅行

[NOIP TG D2T1]旅行

題目大意:$NOIP\;TG\;D2T1$

題解:一棵樹的很簡單,第一個點一定是$1$,只需要對每個節點,找最小的沒有訪問過的節點訪問即可,我寫的是$O(n\log_2n)$。

考慮基環樹的部分,一個顯然的想法是列舉一條環上的邊,然後刪掉,跑樹的部分,複雜度為$O(mn\log_2n)$,明顯過不了。於是考場上的我開始發揚人類智慧,發現有一個環上的邊可以不經過,可以找這一條邊的貢獻,若不經過這條邊的下一位和經過這條邊的下一位進行比較,若不經過較優則不經過這條邊。

出來問了一下,發現可以先把每個點的子樹的兒子排序,再列舉刪除的邊可以做到$O(nm)$,可以過。

卡點:考試時寫的程式碼可能會“多刪除”幾條邊導致出錯,並且僅當環上的點為當前的節點兒子中最大的節點時才會斷這條邊,而考場上沒考慮到。考場上寫了一個環的部分分,沒有考慮到走回去的情況(如果不寫,我的那個假的程式是可以跑對的,也就是說我白白丟了$12$分還花了時間,自閉了)

 

C++ Code:

#include <cstdio>
#include <algorithm>
#include <vector>
#define maxn 5010
inline int min(int a, int b) {return a < b ? a : b;}

int head[maxn], cnt = 1;
struct Edge {
	int to, nxt;
	bool can;
} e[maxn << 1];
inline void add(int a, int b) {
	e[++cnt] = (Edge) {b, head[a], true}; head[a] = cnt;
}
int n, m;

namespace Work1 {
	int ans[maxn], idx;
	void dfs(int u, int fa = 0) {
		ans[++idx] = u;
		std::vector<int> V; V.clear();
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if (v != fa) V.push_back(v);
		}
		std::sort(V.begin(), V.end());
		for (std::vector<int>::iterator it = V.begin(); it != V.end(); it++) {
			dfs(*it, u);
		}
	}
	int main() {
		for (int i = 1, a, b; i < n; i++) {
			scanf("%d%d", &a, &b);
			add(a, b);
			add(b, a);
		}
		dfs(1);
		for (int i = 1; i <= n; i++) {
			printf("%d", ans[i]);
			putchar(i == n ? '\n' : ' ');
		}
		return 0;
	}
}


namespace Work2 {
	int C;
	int ans[maxn], p[maxn], idx;
	bool vis[maxn];
	
	int res[maxn], scc;
	int in_C = 0;
	bool used_C = false;
	
	void dfs(int u, int fa = 0, int last = n + 1) {
		vis[u] = true;
		ans[++idx] = u;
		std::vector<int> V; V.clear();
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if (!vis[v] && v != fa) V.push_back(v);
		}
		std::sort(V.begin(), V.end());
		for (std::vector<int>::iterator it = V.begin(); it != V.end(); it++) if (!vis[*it]) {
			if (res[u] == res[*it]) {
				if (in_C == u) used_C = true;
				if (!in_C) in_C = u;
				if (used_C) {
					dfs(*it, u, n + 1);
				} else {
					if ((it + 1) == V.end()) {
						if (*it < last) dfs(*it, u, last);
						else used_C = true;
					} else dfs(*it, u, *(it + 1));
				}
			} else dfs(*it, u, n + 1);
		}
	}
	
	int DFN[maxn], low[maxn];
	int S[maxn], top;
	void tarjan(int u, int father = 0) {
		DFN[u] = low[u] = ++idx;
		S[++top] = u;
		int v;
		for (int i = head[u]; i; i = e[i].nxt) if (i ^ father ^ 1) {
			v = e[i].to;
			if (!DFN[v]) {
				tarjan(v, i);
				low[u] = min(low[u], low[v]);
			} else low[u] = min(low[u], DFN[v]);
		}
		if (DFN[u] == low[u]) {
			scc++;
			do {
				v = S[top--];
				res[v] = scc;
			} while (v != u);
		}
	}
	
	inline bool check() {
		for (int i = 1; i <= n; i++) if (ans[i] != p[i]) {
			return p[i] < ans[i];
		}
		return false;
	}
	
	void dfs2(int u, int fa = 0) {
		p[++idx] = u;
		std::vector<int> V; V.clear();
		for (int i = head[u]; i; i = e[i].nxt) if (e[i].can) {
			int v = e[i].to;
			if (v != fa) V.push_back(v);
		}
		std::sort(V.begin(), V.end());
		for (std::vector<int>::iterator it = V.begin(); it != V.end(); it++) {
			dfs2(*it, u);
		}
	}
	
	int main() {
		for (int i = 0, a, b; i < n; i++) {
			scanf("%d%d", &a, &b);
			add(a, b);
			add(b, a);
		}
		tarjan(1);
		for (int i = 1; i <= n; i++) ans[i] = n;
		if (n < 500) {
			for (int i = 2; i <= cnt; i += 2) {
				int u = e[i ^ 1].to, v = e[i].to;
				if (res[u] == res[v]) {
					idx = 0;
					e[i].can = e[i ^ 1].can = false;
					dfs2(1);
					if (check()) {
						for (int j = 1; j <= n; j++) ans[j] = p[j];
					}
					e[i].can = e[i ^ 1].can = true;
				}
			}
			for (int i = 1; i <= n; i++) {
				printf("%d", ans[i]);
				putchar(i == n ? '\n' : ' ');
			}
			return 0;
		}
		for (int i = 2; i <= cnt; i += 2) {
			int u = e[i ^ 1].to, v = e[i].to;
			if (res[u] == res[v]) {
				C = res[u];
				break;
			}
		}
		idx = 0;
		dfs(1);
		for (int i = 1; i <= n; i++) {
			printf("%d", ans[i]);
			putchar(i == n ? '\n' : ' ');
		}
		return 0;
	}
}


int main() {
	scanf("%d%d", &n, &m);
	if (n - 1 == m) {
		return Work1::main();
	}
	if (n == m) {
		return Work2::main();
	}
	return 0;
}