圆方树小结

圆方树

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\n", LCA(u, v));
	}
	return 0;
}
posted @ 2020-01-08 20:17  jz929  阅读(133)  评论(0编辑  收藏  举报