圆方树小结
Posted jz929
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了圆方树小结相关的知识,希望对你有一定的参考价值。
圆方树
jzoj 1914. 【2011集训队出题】最短路
这是道圆方树+倍增LCA裸题。
圆方树,顾名思义,就是圆点和方点所组成的树。
而方点就是一个圆的根,一般都是(dfs)时第一个到这个圆的那个位置,然后另附一个点当做方点。然后圆所组成的点都连向方点。
而对于这种圆方边的边权,则为它到根的最近值。
从而将一个仙人掌转成了一棵树。
然后对于这棵树,我们就可以用倍增来求出两两点之间的最短路了。
注意的是,对于最后走到的位置,我们要看看它是从上面绕近还是直接从下面走更优!!!
就这样子了。
(code)
#include <cstdio>
#include <algorithm>
#define N 100010
#define mem(x, a) memset(x, a, sizeof x)
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
using namespace std;
struct node{int v, fr, w;}e[N << 1];
struct edge{int v, fr;}g[N << 1];
int n, n1, m, Q, tail[N], cnt = 1, len[N];
int dfn[N], low[N], fa[N], tot = 0, ri[N];
int f[N][16], l[N][16], cir[N], fz[N], dep[N];
int head[N], cnt1 = 1;
int z[N], top = 0;
inline int read()
{
int x = 0, f = 0; char c = getchar();
while (c < '0' || c > '9') f = (c == '-') ? 1 : f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
inline void add(int u, int v, int w) {e[++cnt] = (node){v, tail[u], w}; tail[u] = cnt;}
inline void add1(int u, int v) {g[++cnt1] = (edge){v, head[u]}; head[u] = cnt1;}
void tarjan(int x)
{
dfn[x] = low[x] = ++tot, z[++top] = x;
for (int p = tail[x], v; p; p = e[p].fr)
{
v = e[p].v;
if (! dfn[v])
{
fa[v] = x, len[v] = e[p].w;
tarjan(v);
low[x] = min(low[x], low[v]);
if (low[v] >= dfn[x])
{
cir[++n] = fz[z[top]], add1(x, n);
int u = top;
while (z[u] != x)
cir[n] += len[z[u]], add1(n, z[u]), u--;
ri[z[u]] = 0, u++;
while (u <= top) ri[z[u]] = ri[z[u - 1]] + len[z[u]], u++;
while (z[top] != x) l[z[top]][0] = min(ri[z[top]], cir[n] - ri[z[top]]), top--;
}
}
else if (dfn[v] < low[x])
fz[x] = e[p].w, low[x] = dfn[v];
}
}
void get_dep(int x)
{
for (int p = head[x], v; p; p = g[p].fr)
v = g[p].v, f[v][0] = x, dep[v] = dep[x] + 1, get_dep(v);
}
int LCA(int x, int y)
{
int ans = 0;
if (dep[x] < dep[y]) swap(x, y);
for (int i = 0, cha = dep[x] - dep[y]; cha; i++, cha >>= 1)
if (cha & 1) ans += l[x][i], x = f[x][i];
if (x == y) return ans;
fd(i, 14, 0)
if (f[x][i] != f[y][i])
{
ans += l[x][i] + l[y][i];
x = f[x][i], y = f[y][i];
}
if (cir[f[x][0]]) return ans + min(abs(ri[x] - ri[y]), cir[f[x][0]] - abs(ri[x] - ri[y]));
else return ans + l[x][0] + l[y][0];
}
int main()
{
n = n1 = read(), m = read(), Q = read();
fo(i, 1, m)
{
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, w);
}
tarjan(1), get_dep(1);
fo(j, 1, 14)
fo(i, 1, n)
{
f[i][j] = f[f[i][j - 1]][j - 1];
l[i][j] = l[i][j - 1] + l[f[i][j - 1]][j - 1];
}
fo(i, 1, Q)
{
int u = read(), v = read();
printf("%d
", LCA(u, v));
}
return 0;
}
以上是关于圆方树小结的主要内容,如果未能解决你的问题,请参考以下文章