分析:
这题超有(e)趣(xin)的。。。(又是一道结论题。。。)
结论:对于一个三角划分(即题目中的划分方式),每次选择一条划分的边,将全图分为两个多边形,再各自选择一条划分的边,递归操作,一定存在某种方案,使得在很小的层数内全部划分为三角形。。。
有了这个结论,这题就很好做了。
可以每次选择那条边后,其左侧的点到达右侧的点一定经过这两个点中的一个,因此只需要把这两个点到达所有其他点的最短路求出来。
询问的时候,一直找到将询问点分为两部分的那一层,然后直接回答即可。
复杂度 O ( ( N + Q ) ∗ 层 数 ) O((N+Q)*层数) O((N+Q)∗层数)
关于这个层数的数量级,博主不严谨地证明认为应该是 l o g 3 2 N log_{\frac 3 2}^N log23N的级别,如果读者有严谨证明,请务必告诉我。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#define SF scanf
#define PF printf
#define MAXN 100010
#define MAXM 200000
using namespace std;
struct node{
map<int,int> S;
vector<int> v;
vector<vector<int> >a;
vector<pair<int,int> > edge;
vector<int> distl,distr;
int L,R;
int pl,pr;
pair<int,int> best_split(){
int d=a.size()+1;
pair<int,int> res;
for(int i=0;i<int(edge.size());i++){
int l=max(edge[i].second-edge[i].first,int(a.size())-edge[i].second+edge[i].first);
if(l<d){
d=l;
res=make_pair(edge[i].first,edge[i].second);
}
}
return res;
}
void add_point(int x){
v.push_back(x);
S[x]=int(v.size())-1;
}
void add_edge(int u,int v){
u=S[u],v=S[v];
if(u>v) swap(u,v);
edge.push_back(make_pair(u,v));
a[u].push_back(v);
a[v].push_back(u);
}
}tree[MAXM];
queue<int> q;
void bfs(int stx,vector<int> &dist,vector<vector<int> > &a){
q.push(stx);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<int(a[x].size());i++){
int u=a[x][i];
if(u!=stx&&dist[u]==0){
dist[u]=dist[x]+1;
q.push(u);
}
}
}
}
int ncnt;
void build(int id){
if(tree[id].a.size()<=3)
return ;
pair<int,int> tmp=tree[id].best_split();
int L=tmp.first,R=tmp.second;
int pl=++ncnt,pr=++ncnt;
int Siz=tree[id].v.size();
tree[id].distl.resize(Siz);
tree[id].distr.resize(Siz);
bfs(L,tree[id].distl,tree[id].a);
bfs(R,tree[id].distr,tree[id].a);
tree[id].L=L,tree[id].R=R;
tree[id].pl=pl,tree[id].pr=pr;
int sizl=(R-L+1);
int sizr=Siz-sizl+2;
tree[pl].a.resize(sizl);
tree[pr].a.resize(sizr);
for(int i=L;i<=R;i++) tree[pl].add_point(tree[id].v[i]);
for(int i=0;i<=L;i++) tree[pr].add_point(tree[id].v[i]);
for(int i=R;i<tree[id].v.size();i++) tree[pr].add_point(tree[id].v[i]);
for(int i=0;i<tree[id].edge.size();i++){
int u=tree[id].edge[i].first;
int v=tree[id].edge[i].second;
if(u>=L&&v<=R)
tree[pl].add_edge(tree[id].v[u],tree[id].v[v]);
if((u<=L&&v<=L)||(u<=L&&v>=R)||(u>=R&&v>=R))
tree[pr].add_edge(tree[id].v[u],tree[id].v[v]);
}
build(pl);
build(pr);
}
int query(int id,int x,int y){
if(tree[id].v.size()==3)
return 1;
int u=tree[id].S[x],v=tree[id].S[y];
if(u>v) swap(u,v);
int L=tree[id].L,R=tree[id].R;
if(u>=L&&u<=R){
if(v>=L&&v<=R)
return query(tree[id].pl,x,y);
return min(tree[id].distl[u]+tree[id].distl[v],tree[id].distr[u]+tree[id].distr[v]);
}
if(u<=L||u>=R){
if(v<=L||v>=R)
return query(tree[id].pr,x,y);
return min(tree[id].distl[u]+tree[id].distl[v],tree[id].distr[u]+tree[id].distr[v]);
}
}
int main(){
// freopen("dri");
int n,u,v,m;
SF("%d",&n);
tree[1].a.resize(n);
for(int i=1;i<=n;i++) tree[1].add_point(i);
for(int i=1;i<n;i++)
tree[1].add_edge(i,i+1);
tree[1].add_edge(1,n);
for(int i=1;i<=n-3;i++){
SF("%d%d",&u,&v);
if(u>v) swap(u,v);
tree[1].add_edge(u,v);
}
ncnt=1;
build(1);
SF("%d",&m);
int x,y;
for(int i=1;i<=m;i++){
SF("%d%d",&x,&y);
if(x==y)
PF("0\n");
else
PF("%d\n",query(1,x,y));
}
}