【NOI2018模拟5】三角剖分Bsh

本文介绍了一种解决三角剖分图中最短路径问题的方法。通过分治策略,选取中间点来平衡图的两侧,利用广度优先搜索计算点间的距离,并解决了大量查询问题。

【NOI2018模拟5】三角剖分Bsh

Description

  给定一个正 n 边形及其三角剖分,共 2n - 3 条边 (n条多边形的边和n-3 条对角线),每条边的长度为 1。
  共 q 次询问,每次询问给定两个点,求它们的最短距离。

Input

  第一行一个整数 n ,表示多边形的点数;
  接下来 n - 3 行,每行两个整数 ui,vi,表示一条 ai 和 bi 之间的对角线;
  接下来一行一个整数 q,表示询问个数;
  接下来 q 行,每行两个整数 xi,yi,表示第 i 次询问的起点和终点;

Output

  对于每一个询问输出一个整数,表示答案。

Sample Input

6
1 5
2 4
5 2
5
1 3
2 5
3 4
6 3
6 6

Sample Output

2
1
1
3
0

\(n\leq 52000,1\leq q\leq 2n\)

因为这是个平面图,我们发现,选取一条边之后可以将图分为两个部分,两个部分之间的最短路一定经过了这条边的两个端点中至少一个。

又因为这是三角剖分,所以我们可以找到中间点使得左右两边的点数非常接近。所以我们可以分治。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 150000

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n,m;
struct road {
    int to,nxt;
}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}

#define pr pair<int,int>
#define mp(a,b) make_pair(a,b)

bool vis[N];
int dis1[N],dis2[N];
queue<int>q;
void bfs(int S,int *dis) {
    q.push(S);
    dis[S]=0;
    while(!q.empty()) {
        int v=q.front();
        q.pop();
        for(int i=h[v];i;i=s[i].nxt) {
            int to=s[i].to;
            if(!vis[to]) continue ;
            if(dis[to]>1e9) {
                dis[to]=dis[v]+1;
                q.push(to);
            }
        }
    }
}

struct query {int x,y,id;};

int pre[N];
int ans[N];
int tag1[N],tag2[N];

int tim;
void solve(vector<int>V,vector<pr>E,vector<query>Q) {
    if(!Q.size()) return ;
    if(V.size()==3) {
        for(int i=0;i<Q.size();i++) {
            int x=Q[i].x,y=Q[i].y;
            if(x==y) ans[Q[i].id]=0;
            else ans[Q[i].id]=1;
        }
        return ;
    }
    int n=V.size(),m=E.size();
    int mid=(n-2)>>1;
    for(int i=0;i<n;i++) pre[V[i]]=0;
    pre[V[0]]=1;
    for(int i=1;i<n;i++) pre[V[i]]=pre[V[i-1]]+1;
    int id;
    int X=0,Y=0;
    int MN=1e9;
    for(int i=0;i<m;i++) {
        int x=E[i].first,y=E[i].second;
        int now=pre[y]-pre[x]-1;
        if(max(now,n-2-now)<MN) {
            MN=max(now,n-2-now);
            X=x,Y=y;
        }
    }
    vector<int>v1,v2;
    vector<pr>e1,e2;
    vector<query>q1,q2;
    v1.clear(),e1.clear(),q1.clear();
    v2.clear(),e2.clear(),q2.clear();
    for(int i=0;i<n;i++) {
        if(X<=V[i]&&V[i]<=Y) tag1[V[i]]=1;
        if(V[i]>=Y||V[i]<=X) tag2[V[i]]=1;
    }
    for(int i=0;i<n;i++) {
        if(tag1[V[i]]) v1.push_back(V[i]);
        if(tag2[V[i]]) v2.push_back(V[i]);
    }
    for(int i=0;i<m;i++) {
        int x=E[i].first,y=E[i].second;
        if(tag1[x]&&tag1[y]) e1.push_back(E[i]);
        if(tag2[x]&&tag2[y]) e2.push_back(E[i]);
    }
    for(int i=0;i<Q.size();i++) {
        if(tag1[Q[i].x]&&tag1[Q[i].y]) q1.push_back(Q[i]);
        if(tag2[Q[i].x]&&tag2[Q[i].y]) q2.push_back(Q[i]);
    }
    for(int i=0;i<n;i++) {
        dis1[V[i]]=dis2[V[i]]=1e9+7;
        vis[V[i]]=1;
    }
    bfs(X,dis1);
    bfs(Y,dis2);
    for(int i=0;i<Q.size();i++) {
        int x=Q[i].x,y=Q[i].y,id=Q[i].id;
        ans[id]=min(ans[id],dis1[x]+dis1[y]);
        ans[id]=min(ans[id],dis2[x]+dis2[y]);
        ans[id]=min(ans[id],dis1[x]+dis2[y]+1);
        ans[id]=min(ans[id],dis2[x]+dis1[y]+1);
    }
    for(int i=0;i<n;i++) tag1[V[i]]=tag2[V[i]]=vis[V[i]]=0;
    solve(v1,e1,q1),solve(v2,e2,q2);
}

vector<int>V;
vector<pr>E;
vector<query>Q;
int main() {
    n=Get();
    for(int i=1;i<n;i++) add(i,i+1),add(i+1,i);
    add(1,n),add(n,1);
    for(int i=1;i<=n-3;i++) {
        int x=Get(),y=Get();
        add(x,y),add(y,x);
        if(x>y) swap(x,y);
        E.push_back(mp(x,y));
    }
    memset(ans,0x3f,sizeof(ans));
    for(int i=1;i<=n;i++) V.push_back(i);
    m=Get();
    for(int i=1;i<=m;i++) {
        int x=Get(),y=Get();
        if(x>y) swap(x,y);
        Q.push_back((query) {x,y,i});
    }
    solve(V,E,Q);
    for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}

转载于:https://www.cnblogs.com/hchhch233/p/10601102.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值