同时需要让下一位尽可能优,就一定要最大化 aj+1,设 k 为最大的 k 使得 ai∼ak 都大于等于 ai,则下一个区间的左端点为 ai∼ak 的最大值。如果 k=i,则这个左端点是 i+1。
回到原问题。
容易发现这个跳的过程类似于给每个点找到唯一的父亲,显然 i 的父亲是 i 左边第一个 xj=xi−1 的 j,因为跳前 xi−1 步时每次肯定是能往后跳就尽量往后跳,所以第 xi−1 步是一定跳到了 j,最后一步为 i。如果找不到这样的 j 就无解。
然后有个引理,是一个合法序列 x 建出来的树,一定满足每个子树构成一段区间。证明就考虑对于一个节点 x,如果已经证明了 x 的子树是一个区间,然后去归纳证明 x 的每个儿子的子树也是区间。
具体地,如果 x 只有 1 个儿子,则这个儿子一定是 x+1,显然还是区间。如果有大于等于 2 个儿子,那么这些儿子的权值一定按升序排列,如果存在一条 u→v 且 u 和 v 对应的祖先不相同,则根据能往后跳就往后跳的原则,前面从 x 开始时一定是先跳 x 的儿子,再进入子树区间,不再出来。这里就矛盾了。
int n; int x[kMaxN], sz[kMaxN], mx[kMaxN], res[kMaxN]; std::vector<int> G[kMaxN];
boolsolve(int l, int r, int a, int b){ if (l == r) return res[l] = a, 1; if (G[l].size() == 1) { res[l] = b; returnsolve(l + 1, r, a, b - 1); } else { for (auto i : G[l]) if (G[i].size() > 1) return0; res[l] = a++; for (auto i : G[l]) { if (!solve(i, i + sz[i] - 1, a, a + sz[i] - 1)) return0; a += sz[i]; } return1; } }
voiddickdreamer(){ staticint lst[kMaxN]; std::cin >> n; for (int i = 0; i <= n; ++i) G[i].clear(), res[i] = lst[i] = 0; for (int i = 1; i <= n; ++i) std::cin >> x[i]; lst[x[1]] = 1; for (int i = 2; i <= n; ++i) { if (!lst[x[i] - 1]) returnvoid(std::cout << "NO\n"); G[lst[x[i] - 1]].emplace_back(i); lst[x[i]] = i; } for (int i = n; i; --i) { sz[i] = 1, mx[i] = i; for (auto j : G[i]) sz[i] += sz[j], mx[i] = std::max(mx[i], mx[j]); if (mx[i] - i + 1 != sz[i]) returnvoid(std::cout << "NO\n"); } if (!solve(1, n, 1, n)) returnvoid(std::cout << "NO\n"); std::cout << "YES\n"; for (int i = 1; i <= n; ++i) std::cout << res[i] << " \n"[i == n]; }