首先我们来想一下,如果两个山顶间有连边,意味着两座山中间没有任何一座山高于这两座山的连线。
加入从左到右三座山分别是p, q, r,这就意味着Kpq >= Kqr,否则q山将是不合法的,这其实就是在维护一个凸包。
接下来的问题是,我们需要能够到达当前能够到达的最右端的山峰,并且在这里我们能和团队中的另一个人相遇。
第一个问题我们可以反向维护一个下凸壳来解决,之后我们就得到了当前山峰能够到达的最右端的山峰。
那么我们将这两个山峰建边,就成功将原问题转化成了树上的问题。
得到一个树后,第二个问题两个人在一个点相遇也就成了LCA问题。
#include <bits/stdc++.h>
using namespace std;
#define all(x) x.begin(), x.end()
typedef long long LL;
const LL maxn = 1e5+100;
LL n;
struct PoLL {
LL x, y;
};
vector<PoLL> V;
LL stk[maxn], top = 0;
LL Cross(PoLL a, PoLL b) {
return a.x*b.y-a.y*b.x;
}
PoLL operator - (PoLL & a, PoLL & b) {
return {a.x-b.x, a.y-b.y};
}
struct LCA {
LL p[maxn][20], n, cnt[maxn], deep[maxn];
vector<LL> G[maxn];
LL dfs(LL u, LL _p) {
p[u][0] = _p;
cnt[u] = 1;
for(LL i = 0; i < G[u].size(); i++) {
LL v = G[u][i];
if(v == _p) continue;
deep[v] = deep[u] + 1;
cnt[u] += dfs(v, u);
}
return cnt[u];
}
void Lca_init(LL _n) {
n = _n;
for(LL i = 0; i < maxn; i++) G[i].clear();
for(LL i = V.size()- 1; i >= 0; i--) {
if(top <= 1)
stk[top++] = i;
else {
while(top > 1 && Cross(V[stk[top-1]]-V[stk[top-2]], V[i]-V[stk[top-1]]) < 0) {
top--;
}
stk[top++] = i;
}
if(top >= 2) {
G[1+stk[top-1]].push_back(stk[top-2]+1);
G[1+stk[top-2]].push_back(stk[top-1]+1);
}
}
deep[n] = 0;
dfs(n, -1);
for(LL j = 1; j < 20; j++) {
for(LL i = 1; i <= n; i++) {
if(p[i][j-1] == -1) p[i][j] = -1;
else p[i][j] = p[p[i][j-1]][j-1];
}
}
}
LL Lca_up(LL u, LL len) {
for(LL i = 0; i < 20; i++) {
if(u != -1 && len&(1<<i))
u=p[u][i];
}
return u;
}
LL Lca(LL u, LL v) {
if(deep[u] < deep[v]) swap(u, v);
u = Lca_up(u, deep[u] - deep[v]);
if(v == u) return u;
for(LL i = 19; i >= 0; i--) {
if(p[u][i] == p[v][i]) continue;
u = p[u][i];
v = p[v][i];
}
return p[u][0];
}
} solver;
int main(){
scanf("%lld", &n);
for(LL i = 1; i <= n; i++) {
LL x, y;
scanf("%lld%lld", &x, &y);
V.push_back({x, y});
}
solver.Lca_init(n);
LL m;
scanf("%lld", &m);
for(LL i = 1; i <= m; i++) {
LL u, v;
scanf("%lld%lld", &u, &v);
printf("%lld ", solver.Lca(u, v));
}
return 0;
}