题解 P5049 【旅行(数据加强版)】

吹雪吹雪吹

2018-11-26 09:22:40

Solution

~~我们选择n方过百万~~ 树的情况很容易解决,一直往编号小的子节点DFS下去就行了。 ``m=n`` 也就是带有一个环。~~这里我们考虑拆边解决~~。 冷静分析一波,我们发现:初始状态下,从一个点走向环上的一个点存在**至多**两种方法。换言之,我们在不断取最小儿子向下走时可以放弃当前子树回溯**一次**。 显然,上述放弃子树反悔祖先是有一定条件的。例如,下图所示情况不能返回祖先,否则当前点的子树将永远无法到达。 ![](https://cdn.luogu.com.cn/upload/pic/44596.png) 再如下图,当前点的子树是环(或子树中有环)也不能立即回溯。 ![](https://cdn.luogu.com.cn/upload/pic/44597.png) 这样也不行呢qwq ![](https://cdn.luogu.com.cn/upload/pic/44598.png) 看来只有这样子才可以暂时放弃子树回溯了:(也就是当前点和当前点的子结点都在环上,当前点的父亲也在环上) ![](https://cdn.luogu.com.cn/upload/pic/44599.png) 当然,满足可回溯条件时我们并不会马上无脑回溯,而会先考虑回溯能否使解变得更优。这个其实很简单,看看未遍历的祖先(及其儿子)中最小的一个是否小于当前点的决策,这条信息在DFS时传递下去即可。 放弃子树的回溯**只能用一次**qwq ```cpp #include <cstdio> #include <cstring> #include <algorithm> #define maxn 500005 #define maxe (maxn << 1) using namespace std; int n, m, stck[maxn], top, cir = 1, in[maxn], cnt; int ans[maxn], to[maxn << 2], s[maxn], idx = 0, len = 0; int lnk[maxn], nxt[maxe], son[maxe], tot = 1; bool vis[maxn], flg[maxn]; int num = 0; inline int read() { char ch = getchar(); int ret = 0, f = 1; while (ch > '9' || ch < '0') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') ret = ret * 10 + ch - '0', ch = getchar(); return ret * f; } void adde(int x, int y) { son[++tot] = y, nxt[tot] = lnk[x], lnk[x] = tot; } void Trump() { memset(flg, 1, sizeof(flg)); for (int i = 1; i <= n; ++i) flg[i] = true; top = 0; for (int i = 1; i <= n; ++i) { if (in[i] == 1) stck[++top] = i; } while (top) { int now = stck[top--]; flg[now] = 0, cnt--; for (int j = lnk[now]; j; j = nxt[j]) { in[son[j]]--; if (in[son[j]] == 1) stck[++top] = son[j]; } } } void DFS(int now, int lst, int pre) { if (!vis[now]) { ans[++idx] = now; if (flg[now]) cnt--; } vis[now] = 1; int l = len + 1, r = len; for (int j = lnk[now]; j; j = nxt[j]) { if (!vis[son[j]]) to[++len] = son[j]; } if (len < l) return; r = len, len++; sort(to + l, to + 1 + r); if (s[now] == 0) s[now] = 1; for (s[now] = l; s[now] <= r; ++s[now]) { if (s[now] == r) { if (flg[to[s[now]]] && flg[now] && flg[pre] && to[s[now]] > lst && cir < 0) { cir = 1; return; } DFS(to[s[now]], lst, now); } else DFS(to[s[now]], to[s[now] + 1], now); } } int main() { freopen("test.in", "r", stdin); freopen("test.out", "w", stdout); n = read(), m = read(); memset(s, 0, sizeof(s)); for (int i = 1; i <= m; ++i) { int x = read(), y = read(); adde(x, y), adde(y, x); in[x]++, in[y]++; } if (n == m) { cir = -1, cnt = n; Trump(); flg[0] = true; } DFS(1, n + 1, 0); for (int i = 1; i <= n; ++i) printf("%d ", ans[i]); return 0; } ``` NOIp的时候脑残,用环上挂着的链来判回溯条件,炸掉3个点QAQ