[NOIP TG D2T1]旅行
阿新 • • 發佈:2018-11-17
題目大意:$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; }