1. 程式人生 > >bzoj4449: [Neerc2015]Distance on 點分治 三角剖分 平面圖與對偶圖

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)

程式碼

常數巨大。

#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; }