题解:P6976 [NEERC 2015] Distance on Triangulation
我们回顾 [ZJOI2016] 旅行者 对于特殊图上求解多源最短路的方法,考虑分治。
我们考虑找到一条边将图分割成两个点集,则由于给定的边是一组三角剖分,因此不存在一条跨过两个点集的边。这时切割面上有两个点,我们以这两个点为起点分别跑一次 BFS 求最短路。
处理询问时,对于跨过这条边的询问,一定会经过这两个点中的至少一个,因此直接用这两个点的最短路更新答案后不再处理。否则用这两个点更新答案后划分到所属的两个点集中的一个递归处理。
接下来说说实现细节,这题还调了挺久的。
由于要保证复杂度正确,我们需要选择一条边使得分成的两个点集的较大者尽可能小。我们枚举所有边,则我们可以容易计算一条边划分成的两个点集的大小,从而找到划分得最均匀得一条边。
在分治的时候,我们可以直接按顺序记录当前点集最外面一圈的顺序。因此我们也可以轻易得到一个点或一条边属于哪个点集。
我实现的时候比较拙劣,每次传入三个 std::vector 分别存储点集、边集、询问集合。这样会 MLE。我们可以在传入一些不会改变 vector 中值的函数时,只传入它的地址。
这里还有一个黑科技。vector 在 clear() 的时候不会释放已经分配的空间。这个时候可以使用 shrink_to_fit() 释放冗余的空间。这样就可以使空间重复利用,就可以通过啦!
那么这道题就做完了,复杂度 \(O(n \log n)\)。
#include<bits/stdc++.h>
#define endl '\n'
#define N 50006
using namespace std;
int n,q,dis[N],id[N],ans[N<<1];
vector<int> G[N];
struct Edge{int u,v;};
struct Query{int u,v,id;};
void build(vector<int> &v,vector<Edge> &e)
{
for(int i:v)
G[i].clear(),G[i].shrink_to_fit();
int sv=v.size();
for(int i=0;i<sv;i++)
G[v[i]].push_back(v[(i+1)%sv]);
for(int i=0;i<sv;i++)
G[v[i]].push_back(v[(i-1+sv)%sv]);
for(auto i:e)
G[i.u].push_back(i.v),G[i.v].push_back(i.u);
}
void bfs(vector<int> &v,int s,int *d)
{
queue<int> q;
for(int i:v)d[i]=1e9;
d[s]=0,q.push(s);
while(q.size())
{
int u=q.front();q.pop();
for(int v:G[u])if(d[v]>d[u]+1)
d[v]=d[u]+1,q.push(v);
}
}
void solve(vector<int> v,vector<Edge> e,vector<Query> ask)
{
if(v.size()<=3)
{
for(auto i:ask)
ans[i.id]=(i.u!=i.v);
return;
}
int sv=v.size();
for(int i=0;i<sv;i++)id[v[i]]=i;
int minn=1e9,du,dv;
for(auto &i:e)
{
if(id[i.u]>id[i.v])swap(i.u,i.v);
int len=id[i.v]-id[i.u]+1;
len=max(len,sv-len+2);
if(len<minn)minn=len,du=i.u,dv=i.v;
}
build(v,e),bfs(v,du,dis);
for(auto i:ask)
ans[i.id]=min(ans[i.id],dis[i.u]+dis[i.v]);
bfs(v,dv,dis);
for(auto i:ask)
ans[i.id]=min(ans[i.id],dis[i.u]+dis[i.v]);
vector<Edge> te;
vector<Query> task;
vector<int> tv;
for(int i=0;i<sv;i++)
if(i<=id[du]||i>=id[dv])tv.push_back(v[i]);
for(auto i:e)
if((id[i.u]<=id[du]||id[dv]<=id[i.u])&&
(id[i.v]<=id[du]||id[dv]<=id[i.v]))te.push_back(i);
for(auto i:ask)
if((id[i.u]<=id[du]||id[dv]<=id[i.u])&&
(id[i.v]<=id[du]||id[dv]<=id[i.v]))task.push_back(i);
solve(tv,te,task),tv.clear(),te.clear(),task.clear();
for(int i=0;i<sv;i++)id[v[i]]=i;
for(int i=0;i<sv;i++)
if(id[du]<=i&&i<=id[dv])tv.push_back(v[i]);
for(auto i:e)
if((id[du]<=id[i.u]&&id[du]<=id[i.v])&&
(id[i.u]<=id[dv]&&id[i.v]<=id[dv]))te.push_back(i);
for(auto i:ask)
if((id[du]<=id[i.u]&&id[du]<=id[i.v])&&
(id[i.u]<=id[dv]&&id[i.v]<=id[dv]))task.push_back(i);
solve(tv,te,task);
tv.shrink_to_fit(),te.shrink_to_fit(),task.shrink_to_fit();
}
main()
{
vector<int> v;
vector<Edge> e;
vector<Query> ask;
scanf("%d",&n);
for(int i=1;i<=n;i++)v.push_back(i);
for(int i=1,u,v;i<=n-3;i++)
scanf("%d%d",&u,&v),e.push_back({u,v});
scanf("%d",&q);
for(int i=1,u,v;i<=q;i++)
scanf("%d%d",&u,&v),ask.push_back({u,v,i});
memset(ans,0x3f,sizeof(ans)),solve(v,e,ask);
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号