题解:P5803 [SEERC 2019] Tree Permutations
posted on 2025-02-22 00:12:01 | under | source
题意:有一个 \(n\) 个点的树,根为 \(1\)。记 \(p_i<i\) 为 \(i\) 的父亲,\(w_i\) 为父亲边权。现将 \(p,w\) 随机打乱得到 \(a\),对每个 \(k\in [1,n)\),当 \(1\to n\) 路径上有 \(k\) 条边,输出最长长度或报告无解。\(n\le 10^5\)。
将 \(a\) 排序。首先初始思路是找出 \(x_1\dots x_k\),其中 \(x_1=1,x_i<x_{i+1}\),也就是 \(1\to n\) 的链,然后在剩下的元素中贪心选取最小的构成 \(p\)。那么问题是怎么确定 \(x\)。
尝试观察性质,不断强化条件,从而推出正解:
- \(\forall i,a_{i}<i\) 否则无解。显然。
- 若 \(a_i=i\),则 \(i\) 一定在 \(1\to n\) 路径上。证明:对于 \(j\in [2,i]\),只能选取 \(a_1\dots a_{i-1}\) 作为父亲节点,所以 \(j\in [i+1,n]\) 无法一步跳到 \([1,i-1]\),所以必然经过 \(i\)。
- \(k=\sum [a_i=i]\) 时一定有解。证明:首先让他们构成 \(1\to n\) 的链,然后考虑剩下的点,对于 \(j\in (x_i,x_{i+1})\),都有 \(a_j<j\),所以一定有解。构造方案即为选取剩下的 \(a\) 中最小的那些。
- 记 \(mx\) 为 \(a\) 不同元素个数,则有 \(k\le mx\)。显然。
- 对于 \(\sum[a_i=i]\le k\le mx\) 均有解。证明:增量法,每次选取剩下元素中最小且与 \(x\) 不同的元素加入 \(1\to n\) 链上,则剩下的节点一定有相应的 \(p\) 可匹配。因为考虑拿走的元素 \(y\),设 \(q\) 为 \(y\) 此前对应的 \(p\),设拿走 \(y\) 后节点 \(z\) 失去对应 \(p\),由于 \(q<y<z\) 所以让 \(z\) 与 \(q\) 匹配即可。
综上,恰在区间内的 \(k\) 才有解。每次删去新增元素,并选取剩下的前 \(k\) 大作为链边权即可。可以做到 \(O(n)\),但我偷懒用线段树 \(O(n\log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5;
int n, m, a[N], buc[N], cnt;
bool vis[N];
vector<int> del;
namespace Sg_Tree{
#define lt (u << 1)
#define rt (u << 1 | 1)
#define mid (l + r >> 1)
int t[N << 2], s[N << 2];
inline void psup(int u) {t[u] = t[lt] + t[rt], s[u] = s[lt] + s[rt];}
inline void upd(int u, int l, int r, int k, int p){
if(l == r) {t[u] += k * p, s[u] += p; return ;}
if(k <= mid) upd(lt, l, mid, k, p);
else upd(rt, mid + 1, r, k, p);
psup(u);
}
inline int fid(int u, int l, int r, int k){
if(l == r) return l * k;
if(s[rt] <= k) return t[rt] + fid(lt, l, mid, k - s[rt]);
return fid(rt, mid + 1, r, k);
}
}using namespace Sg_Tree;
signed main(){
cin >> n, m = 2 * n - 2;
for(int i = 1; i <= m; ++i) scanf("%d", &a[i]), ++buc[a[i]], upd(1, 1, n, a[i], 1);
sort(a + 1, a + 1 + m);
for(int i = 1; i <= m; ++i){
if(a[i] > i) {for(int i = 1; i < n; ++i) printf("-1 "); return 0;}
if(a[i] == i){
vis[i] = true;
--buc[a[i]], upd(1, 1, n, a[i], -1), ++cnt;
}
}
for(int i = 1; i < cnt; ++i) printf("-1 ");
if(cnt) printf("%lld ", fid(1, 1, n, cnt));
for(int i = 1; i < n; ++i)
if(buc[i] && !vis[i]){
upd(1, 1, n, i, -1), ++cnt;
printf("%lld ", fid(1, 1, n, cnt));
}
for(int i = cnt + 1; i < n; ++i) printf("-1 ");
return 0;
}

浙公网安备 33010602011771号