注意到距离最大的点一定是叶子节点。
那么按 (D_i) 从大到小枚举节点,找到它的父亲 (u),直接加一条边。注意到这种做法是必要但不充分的,于是最后再重新跑一遍换根 dp 求出距离和判断和 (D_i) 是否相等即可。
具体的,假设当前节点为 (v),其子树大小为 (sz_v),它的父亲为 (u),那么要满足 (D_u=D_v+sz_v-(n-sz_v))。二分找到 (u) 之后要记得更新 sz[u] += sz[v]
。
代码:
#include <bits/stdc++.h>
#define DC int T = gi <int> (); while (T--)
#define DEBUG fprintf(stderr, "Passing [%s] line %d
", __FUNCTION__, __LINE__)
#define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair <int, int> PII;
typedef pair <LL, int> PLI;
typedef pair <LL, LL> PLL;
template <typename T>
inline T gi()
{
T x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return f * x;
}
template <typename T>
inline T abs(T x) {return x < 0 ? -x : x;}
const int N = 100003, M = N << 1;
int n;
LL d[N];
int sz[N];
PLI p[N];
vector <int> g[N];
LL nd[N];
int nsz[N];
void dfs(int u, int f, int dis)
{
nd[1] += dis, nsz[u] = 1;
for (auto v : g[u]) if (v != f) dfs(v, u, dis + 1), nsz[u] += nsz[v];
}
void dfs1(int u, int f) {for (auto v : g[u]) if (v != f) nd[v] = nd[u] + n - 2 * nsz[v], dfs1(v, u);}
void dfs_print(int u, int f) {for (auto v : g[u]) if (v != f) printf("%d %d
", u, v), dfs_print(v, u);}
int main()
{
//freopen(".in", "r", stdin); freopen(".out", "w", stdout);
n = gi <int> ();
for (int i = 1; i <= n; i+=1) d[i] = gi <LL> (), p[i] = {d[i], i}, sz[i] = 1;
sort(p + 1, p + 1 + n);
for (int i = n; i > 1; i-=1)
{
LL tmp = p[i].fi - n + 2 * sz[p[i].se];
int id = lower_bound(p + 1, p + 1 + n, mp(tmp, 0)) - p;
if (p[id].fi != tmp) return puts("-1"), !!0;
g[p[i].se].pb(p[id].se), g[p[id].se].pb(p[i].se);
sz[p[id].se] += sz[p[i].se];
}
dfs(1, 0, 0);
dfs1(1, 0);
for (int i = 1; i <= n; i+=1) if (nd[i] != d[i]) return puts("-1"), !!0;
dfs_print(1, 0);
return !!0;
}
启发:
- 造树题要想一些特殊情况,如叶子、重心等。