Loading

P5803 [SEERC2019] Tree Permutations 题解

性质结论题。

思路

以下的性质都是在 \(a_i\) 有序的情况下拥有的。

性质一:

有解的必要条件为 \(\forall i\in[1,n-1],a_i\le i\)

证明:
如果存在 \(a_i>i\),那么点 \(i+1\) 在前 \(i\) 个点合法的前提下无法找到一个父亲,所以无法形成一棵树。

性质二:

在有解的情况下,对于 \(a_i=i,i\in[1,n-1]\),点 \(i\) 必定在 \(1\sim n\) 的路径上。

证明:
如果 \(a_i=i\),那么点 \(i+1\) 及后面的点的父亲节点的编号必然不小于 \(i\),所以一定会经过点 \(i\)

性质三:

\(1\sim n\) 的路径上的点数不会超过 \(a\) 的种类数。

证明:
显然。

推论:

在满足性质一,性质二的前提下,任意一条合法的 \(1\sim n\) 的路径都可以对应到一个树上。

所以我们就只需要贪心的往路径中添加最小的点,查询最大的点的和就可以了。

时间复杂度:\(O(n\log n)\)

Code

/*
  ! 以渺小启程,以伟大结束。
  ! Created: 2024/06/29 20:55:24
*/
#include <bits/stdc++.h>
using namespace std;

#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)

const int N = 200010;
const int M = 131072;

using i64 = long long;

int n, m, tp, ct, a[N], b[N], v[N];
int sz[M << 1];
i64 vl[M << 1], ans[N];

inline void FAIL() { fro(i, 1, n - 1) cout << -1 << " "; exit(0); }
inline void add(int x, int c) { x += M; while (x) vl[x] += c, sz[x]++, x >>= 1; }
inline void del(int x, int c) { x += M; while (x) vl[x] -= c, sz[x]--, x >>= 1; }
inline auto ask(int p) {
  i64 t = 1, res = 0;
  while (p != 0) {
    if (t > M) res += (t - M) * p, p = 0;
    else if (p == sz[t]) res += vl[t], p -= sz[t];
    else if (p <= sz[t << 1 | 1]) t = t << 1 | 1;
    else res += vl[t << 1 | 1], p -= sz[t << 1 | 1], t = t << 1;
  }
  return res;
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n, m = n * 2 - 2;
  fro(i, 1, m) cin >> a[i], b[a[i]] = 1;
  sort(a + 1, a + m + 1);
  fro(i, 1, m) add(a[i], a[i]);
  fro(i, 1, n - 1) if (a[i] > i) FAIL();
  fro(i, 1, n - 1) if (a[i] == i) v[i] = 1, ct++, del(i, i);
  fill(ans + 1, ans + n, -1);
  fro(i, 1, n - 1) {
    if (b[i] && v[i] == 0) del(i, i), v[i] = 1, ct++;
    ans[ct] = ask(ct);
  }
  fro(i, 1, n - 1) cout << ans[i] << " \n"[i == n - 1];
  return 0;
}
posted @ 2024-06-30 16:24  JiaY19  阅读(2)  评论(0编辑  收藏  举报