题目大意 : 给出 (n) 个点构成的一棵树,以及平面上 (n) 个点的坐标,现在要求出一种方案,给平面上每个点一个不同的编号,对于每个平面上的点按照编号连树边,使得最终得到的边在平面上两两不相交
$1 leq n leq 1500, -10^9 leq x_i, y_ileq10^9 $
解题思路 :
观察发现,要使得最后连接的树边不相交,对于树中相邻的儿子节点对应的子树,它们在平面上对应的区域不能相交
换句话说,对于任意一棵子树内的连边,其在平面上的向量不能于其他子树内的向量相交
那么问题就转化为给每一个子树 (u) 安排 (sz_u) 个合法的点并且满足上述条件
考虑分治来处理这个问题,每棵子树对应的区间如果在极角排序的结果上是有序且连续的,那么子树间的向量一定不会相交
所以每次选取当前区间内最左下角的点,将其作为当前子树节点根节点对应的点,并以其为基准进行极角排序
对于它的每一个儿子 (v),在区间内划分一段大小为 (sz_v) 的子区间递归处理,可以证明这样一定有解
考虑这样做最多递归 (n) 层,每一层要处理的区间最多大小是 (n), 总复杂度是 (O(n^2logn))
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define N (500005)
vector<int> g[N];
int sz[N], Ans[N], n;
struct Point{ double x, y; int id; } a[N], O;
inline double cross(double X1, double X2, double Y1, double Y2){
return X1 * Y2 - X2 * Y1;
}
inline bool cmp(Point A, Point B){
return cross(A.x - O.x, A.y - O.y, B.x - O.x, B.y - O.y) > 0;
}
inline void dfs(int u, int fa){
sz[u] = 1;
for(int i = 0; i < g[u].size(); i++){
int v = g[u][i];
if(v != fa) dfs(v, u), sz[u] += sz[v];
}
}
inline void solve(int u, int fa, int l, int r){
int co = l;
for(int i = l + 1; i <= r; i++)
if(a[i].y < a[co].y || a[i].y == a[co].y && a[i].x < a[co].x) co = i;
if(co != l) swap(a[co], a[l]);
O = a[l], Ans[a[l].id] = u;
sort(a + l + 1, a + r + 1, cmp);
int pos = l + 1;
for(int i = 0; i < g[u].size(); i++){
int v = g[u][i];
if(v != fa)
solve(v, u, pos, pos + sz[v] - 1), pos += sz[v];
}
}
int main(){
read(n);
for(int i = 1, x, y; i < n; i++){
read(x), read(y);
g[x].push_back(y), g[y].push_back(x);
}
for(int i = 1; i <= n; i++)
scanf("%lf%lf", &a[i].x, &a[i].y), a[i].id = i;
dfs(1, 0), solve(1, 0, 1, n);
for(int i = 1; i <= n; i++) printf("%d ", Ans[i]);
return 0;
}