【大联盟】20230509 T2 树(tree) 题解 P5803 【[SEERC2019] Tree Permutations】
大家可以猜猜看为什么有两个标题,因为这个原因本文就不设密码了。
题目描述
一个 \(n\) 个点的有根树,以 \(1\) 为根,记 \(i\) 的父亲为 \(p_i\),连接 \(i\) 与 \(p_i\) 的边的边权为 \(w_i\),(其中保证 \(p_i<i\))那么这个树可以用 \(p_i\) 和 \(w_i\) 构成的长为 \(2n-2\) 的序列来表示。
因为某些原因,我们遗失了这个树,具体来说,由 \(p_i\) 和 \(w_i\) 构成的序列被打乱了,因此只剩下一个包含 \(2n-2\) 个数的可重集合。但是我们还是能从中获得一些有用的信息,如 \(1\) 到 \(n\) 的可能最长距离。这里两点间的距离定义为从一个节点到另一个节点经过的简单路径的边的边权和。
你需要求出对于所有 \(1\le k < n\),当 \(n\) 与 \(1\) 之间恰有 \(k\) 条边时,可能的最长距离。
数据范围:\(1\le n\le 10^5\)。
题解
赛时得分:0/0。
写的 \(75\) 分的平方,对拍拍挂了,然后不会改,最后对拍的暴力还没交。
对于每个位置计算 \(a_i\),值为 \([1,i-1]\) 在原序列的总出现次数 - \(i-2\),那么 \(i\) 能找到父亲,当且仅当 \(a_i>0\)。
我们假设从 \(1\) 到 \(n\) 的路径是序列 \(b_1,b_2,\cdots,b_k\),那么相当于是对 \((b_1,b_2), (b_2,b_3),\cdots,(b_{k-1},b_k)\) 整体减一。
所以,为了保证方案存在,\(a_i=1\) 的一定要在从 \(1\) 到 \(n\) 的路径上。
于是,线段数维护插入、删除,求前 \(k\) 大即可。
代码
#include <bits/stdc++.h>
#define ls num << 1
#define rs ls | 1
#define li ls, l, mid
#define ri rs, mid + 1, r
#define int long long
#define SZ(x) (int) x.size() - 1
#define all(x) x.begin(), x.end()
#define ms(x, y) memset(x, y, sizeof x)
#define F(i, x, y) for (int i = (x); i <= (y); i++)
#define DF(i, x, y) for (int i = (x); i >= (y); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T> void chkmax(T& x, T y) { x = max(x, y); }
template <typename T> void chkmin(T& x, T y) { x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
const int N = 2e5 + 10;
int n, m, a[N], s[N], d[N], ss, ans, sz[N * 4], sum[N * 4];
vector <int> v, t;
void pushup(int num) {
sz[num] = sz[ls] + sz[rs];
sum[num] = sum[ls] + sum[rs];
}
void modify(int num, int l, int r, int x, int y) {
if (l == r) {
sz[num] += y;
sum[num] += y * l;
return;
} int mid = (l + r) >> 1;
if (mid >= x) modify(li, x, y);
else modify(ri, x, y);
pushup(num);
}
void add(int x) {
v.push_back(x);
modify(1, 1, n, x, -1);
}
int query(int num, int l, int r, int x) {
// cout << num << " " << l << " " << r << " " << x << " " << sz[num] << " " << sum[num] << endl;
if (l == r) return l * x;//sum[num];
int mid = (l + r) >> 1;
if (sz[rs] >= x) return query(ri, x);
return query(li, x - sz[rs]) + sum[rs];
}
signed main() {
read(n); m = 2 * n - 2;
F(i, 1, m) read(a[i]), s[a[i]]++, modify(1, 1, n, a[i], 1);
sort(a + 1, a + m + 1);
d[1] = 1;
add(1);
F(i, 2, n) {
d[i] = d[i - 1] + s[i - 1] - 1;
// cout << i << " " << d[i] << endl;
if (d[i] <= 0) {
F(i, 1, n - 1) cout << -1 << " ";
return 0;
}
if (d[i] == 1) add(i);
else if (s[i]) t.push_back(i);
}
// for (int i: v) cout << i << " "; cout << endl;
// for (int i: t) cout << i << " "; cout << endl;
// cout << v.size() << " " << t.size() << endl;
F(i, 1, v.size() - 1) cout << -1 << " ";
F(i, v.size(), v.size() + t.size()) {
// cout << i << " " << query(1, 1, n, i) << endl;
cout << query(1, 1, n, i) << " ";
// return 0;
if (i != v.size() + t.size()) modify(1, 1, n, t[i - v.size()], -1);
}
F(i, v.size() + t.size() + 1, n - 1) cout << -1 << " ";
return 0;
}