bzoj4449: [Neerc2015]Distance on 点分治 三角剖分 平面图与对偶图

bzoj4449: [Neerc2015]Distance on Triangulation

Description

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

Input**

第一行一个整数n(n <= 50000),代表有n个点。点1,2,3,…,n是凸多边形上是顺时针排布的。
接下来n-3行,每行两个整数(x,y),代表(x,y)之间有一条剖分边。
接下来是一个整数q(q <= 100000),代表有q组询问。
接下来q行是两个整数(a,b)。

Output**

输出q行,每行一个整数代表最少边数。

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

分析

平面图与对偶图的转化-链接
三角剖分的对偶图是一棵树。
考虑到对偶图中的环和割是一一对应的。
说白了就是把其中一个三角形删了,必定会把平面图分成两块。这两块不联通。
所以这分属两块的询问点必定经过这个三角形中的某一个节点。
也就是说可以单独考虑过对偶图上的经过某个三角形的路径。上点分治即可。
每次Dfs处理出所有当前块内的点,然后Bfs求到三个点的距离,如果两个点分属两个不同的块,直接采用这次的答案,否则递归求解。
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码

常数巨大。

#include<bits/stdc++.h>
const int N = 2e6 + 10;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
struct Edge {
    int pr[N], nx[N], to[N], tp;
    void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
    void adds(int u, int v) {add(u, v); add(v, u);}
}G, T, Q;
struct E {int x, y, z;}e[N];
bool cmp(E a, E b) {return a.x == b.x ? a.y < b.y : a.x < b.x;}
int p[N][3], D[3][N], sz[N], d[N], q[N], st[N], mk[N], bmk[N];
int be[N], qu[N], qv[N], ans[N], tm, btm, sums, mn, Rt, m, n, cnt; bool vis[N], in[N];
void Ins(int x, int y, int z) {e[++m] = (E){x, y, z};}
void Ins(int x, int id) {
    for(int i = 0;i < 3 && x; ++i) 
        if(x > p[id][i]) std::swap(p[id][i], x);
    if(p[id][2]) {
        Ins(p[id][0], p[id][1], id);
        Ins(p[id][0], p[id][2], id); 
        Ins(p[id][1], p[id][2], id);
    }
}
void Build() {
    int L = 1, R = 0;
    for(int i = 1;i <= n; ++i) if(d[i] == 2) q[++R] = i; cnt = 0;
    for(int u = q[L];L <= R; u = q[++L]) 
    if(d[u] == 2) {
        Ins(u, ++cnt); in[u] = true;
        for(int i = G.pr[u]; i; i = G.nx[i]) {
            if((--d[G.to[i]]) == 2) q[++R] = G.to[i];
            if(!in[G.to[i]]) Ins(G.to[i], cnt);
        }
    }
    std::sort(e + 1, e + m + 1, cmp);
    for(int i = 1;i < m; ++i) 
        if(e[i].x == e[i + 1].x && e[i].y == e[i + 1].y) 
            T.adds(e[i].z, e[i + 1].z);
}
void G_get(int u, int fa) {
    sz[u] = 1; int tp = 0;
    for(int i = T.pr[u], v; i; i = T.nx[i])
        if(!vis[v = T.to[i]] && v != fa)
            G_get(v, u), tp = std::max(tp, sz[v]), sz[u] += sz[v];
    tp = std::max(tp, sums - sz[u]);
    if(tp < mn) mn = tp, Rt = u;
}
void Bfs(int x, int *D) {
    int L = 1, R; q[R = 1] = x; D[x] = 0; bmk[x] = ++btm;
    for(int u = q[L];L <= R; u = q[++L])
        for(int i = G.pr[u], v; i; i = G.nx[i])
            if(mk[v = G.to[i]] == tm && bmk[v] != btm) 
                bmk[v] = btm, q[++R] = v, D[v] = D[u] + 1;
}
void Dfs(int u, int fa, int rt) {
    for(int k = 0;k < 3; ++k)
        mk[p[u][k]] = tm, be[p[u][k]] = rt;
    for(int i = T.pr[u]; i; i = T.nx[i])
        if(T.to[i] != fa && !vis[T.to[i]]) 
            Dfs(T.to[i], u, rt);        
}
void Work(int x, int cs) {
    if(!Q.pr[x]) return ;
    mn = 1e9; sums = cs; G_get(x, 0); vis[Rt] = true; ++tm;
    for(int j = T.pr[Rt]; j; j = T.nx[j])
        if(!vis[T.to[j]]) 
            Dfs(T.to[j], 0, T.to[j]);
    for(int k = 0;k < 3; ++k)
        mk[p[Rt][k]] = tm, be[p[Rt][k]] = Rt;
    for(int k = 0;k < 3; ++k) 
        Bfs(p[Rt][k], D[k]);
    int tp = 0; for(int i = Q.pr[x]; i; i = Q.nx[i]) st[++tp] = Q.to[i]; Q.pr[x] = 0; 
    for(int i = 1;i <= tp; ++i) {
        int nw = st[i], u = qu[st[i]], v = qv[st[i]];
        if(be[u] == be[v]) {
            if(be[u] == Rt) ans[nw] = 1;
            else Q.add(be[u], nw);
        }
        else {
            int r = 1e9;
            for(int k = 0;k < 3; ++k) 
                r = std::min(r, D[k][u] + D[k][v]);
            ans[nw] = r;
        }
    }
    for(int i = T.pr[Rt]; i; i = T.nx[i]) 
        if(!vis[T.to[i]]) 
            Work(T.to[i], sz[T.to[i]] > sz[Rt] ? cs - sz[Rt] : sz[T.to[i]]);
}
int main() {
    n = ri();
    for(int i = 1;i <= n; ++i) G.adds(i, i % n + 1), d[i] = 2;
    for(int i = 1, x, y;i <= n - 3; ++i) x = ri(), y = ri(), ++d[x], ++d[y], G.adds(x, y);
    Build(); int q = ri();
    for(int i = 1;i <= q; ++i) {
        qu[i] = ri(), qv[i] = ri();
        if(qu[i] != qv[i]) Q.add(1, i);
    }
    Work(1, cnt);
    for(int i = 1;i <= q; ++i) printf("%d\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值