[NEERC 2015] bzoj4449 Distance on Triangulation [分治+最短路]

博客介绍了如何解决一个凸n边形及其三角剖分中的距离查询问题。通过采用分治策略,选取最均匀的对角线进行划分,并更新跨过对角线的询问,以nlogn的时间复杂度找到所有询问的最短路径。文章强调了分治过程中对角线、点和询问的记录,以及在最短路计算时只处理当前区间内的点的重要性。此外,还提到了分治策略的核心思想,即通过预处理降低复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description:
给定一个凸nn边形,以及它的三角剖分。再给定q个询问,每个询问是一对凸多边行上的顶点(a,b)(a,b),问点aa最少经过多少条边(可以是多边形上的边,也可以是剖分上的边)可以到达点b


Solution:
看起来很难写,其实想清楚还是比较好写的。
我们可以对每个多边形进行分治,每次选择其中的一个分的最均匀的对角线,以其两个顶点做最短路,并更新跨过这条对角线的所有询问,这些询问取到最小值肯定也是经过这条对角线。这样分治只需要nlognnlogn的时间可以解决所有询问。分治时要记录当前多边形中的对角线,点,还有询问,求最短路时只要把当前区间里的点求最短路,否则时间复杂度不对。点则是为了在求最短路和分治区间时判断是否属于当前区间。
代码几乎是抄的,主要是结构。
分治主要思想是把一些有相同点的问题通过预处理批量处理,比如这里就是经过对角线的询问,先预处理出两点的最短路,复杂度很低,然后其他的就可以批量预处理了。
cdq分治也是,找出一些操作,其中一部分都受到另一部分影响,所以把前一部分通过数据结构预处理,这样就不用两两重复操作了。


#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5, inf = 1e9;
struct data {
    int l, r, id;
} q[N], E[N], tmp1[N], tmp2[N], tmp3[N], tmp4[N];
int n, m;
int ans[N], B[N], d1[N], d2[N], tmp5[N], tmp6[N];
vector<int> G[N];
queue<int> que;
int check(int l, int r, int x) {
    return lower_bound(B + l, B + r + 1, x) - B;
}
void bfs(int l, int r, int s, int *d) {
    for(int i = l; i <= r; ++i) {
        d[B[i]] = inf;
    }
    d[s] = 0;
    que.push(s);
    while(!que.empty()) {
        int u = que.front();
        que.pop();
        for(int i = 0; i < G[u].size(); ++i) {
            int v = G[u][i];
            if(d[v] == inf && B[check(l, r, v)] == v) {
                d[v] = d[u] + 1;
                que.push(v);
            }
        }
    }
}
int get_ans(int u, int v) {
    return min(min(d1[u] + d1[v], d2[u] + d2[v]), min(d1[u] + 1 + d2[v], d1[v] + 1 + d2[u]));
}
void cdq(int l, int r, int x, int y, int a, int b) {
    if(l > r || x > y) {
        return;
    }
    int mn = inf, pos;
    for(int i = l; i <= r; ++i) {
        int p = check(x, y, E[i].l), q = check(x, y, E[i].r);
        if(p > q) {
            swap(p, q);
        }
        int tmp = max(q - p + 1, y - x + 1 - q + p);
        if(tmp < mn) {
            mn = tmp;
            pos = i;
        }
    }
    int L = E[pos].l, R = E[pos].r, tot1 = 0, tot2 = 0, tot3 = 0, tot4 = 0, tot5 = 0, tot6 = 0;
    bfs(x, y, L, d1);
    bfs(x, y, R, d2);
    for(int i = a; i <= b; ++i) {
        if(q[i].l == L && q[i].r == R) {
            ans[q[i].id] = 1;
            continue;
        }
        ans[q[i].id] = min(ans[q[i].id], get_ans(q[i].l, q[i].r));
        if(q[i].l > L && q[i].r < R) {
            tmp1[++tot1] = q[i];
        } else if((q[i].l > R && q[i].r > R) || (q[i].l < L && q[i].r > R) || (q[i].r < L)) {
            tmp2[++tot2] = q[i];
        }
    }
    for(int i = 1; i <= tot1; ++i) {
        q[a + i - 1] = tmp1[i];
    }
    for(int i = 1; i <= tot2; ++i) {
        q[a + tot1 + i - 1] = tmp2[i];
    }
    for(int i = l; i <= r; ++i) {
        if(i != pos) {
            if(E[i].l >= L && E[i].r <= R) {
                tmp3[++tot3] = E[i];
            } else {
                tmp4[++tot4] = E[i];
            }
        }
    }
    for(int i = 1; i <= tot3; ++i) {
        E[l + i - 1] = tmp3[i];
    }
    for(int i = 1; i <= tot4; ++i) {
        E[l + tot3 + i - 1] = tmp4[i];
    }
    for(int i = x; i <= y; ++i) {
        if(B[i] >= L && B[i] <= R) {
            tmp5[++tot5] = B[i];
        } else {
            tmp6[++tot6] = B[i];
        }
    } 
    for(int i = 1; i <= tot5; ++i) {
        B[x + i - 1] = tmp5[i];
    }
    for(int i = 1; i <= tot6; ++i) {
        B[x + tot5 + i - 1] = tmp6[i];
    }
    cdq(l, l + tot3 - 1, x, x + tot5 - 1, a, a + tot1 - 1);
    cdq(l + tot3, l + tot3 + tot4 - 1, x + tot5, x + tot5 + tot6 - 1, a + tot1, a + tot1 + tot2 - 1);
}
int main() {
//  freopen("distance.in", "r", stdin);
//  freopen("distance.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n - 3; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        if(u > v) {
            swap(u, v);
        }
        G[u].push_back(v);
        G[v].push_back(u);
        E[i].l = u;
        E[i].r = v;
    }
    for(int i = 1; i <= n; ++i) {
        B[i] = i;
        G[i].push_back(i % n + 1);
        G[i % n + 1].push_back(i);
    }
    scanf("%d", &m);
    for(int i = 1; i <= m; ++i) {
        scanf("%d%d", &q[i].l, &q[i].r);
        q[i].id = i;
        if(q[i].l > q[i].r) {
            swap(q[i].l, q[i].r);
        }
        ans[i] = min(q[i].r - q[i].l, n + q[i].l - q[i].r);
    }
    cdq(1, n - 3, 1, n, 1, m);
    for(int i = 1; i <= m; ++i) {
        printf("%d\n", ans[i]);
    }
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值