题目链接 BZOJ 2125 最短路 圆方树
求一幅仙人掌图中,Q次询问两点最短路。
仙人掌问题,我们可以直接将原来的N个点缩点成为一棵生成树——圆方树。
这棵圆方树是怎样建立的呢,首先,我们看图:
这个是原图;
这个是缩点之后的圆方树。
那么,两点之间的距离不就是可以通过LCA来求解了吗?但是这里有个问题,就是我们在求解LCA的时候,也是最关键的一点就是在于,我们最后的最近公共祖先LCA是圆点还是方点是不一样的!
如果是圆点,那么就是代表着我们求得的是固定的通路了,此时就是答案了(处理LCA的时候,别忘了还要把边权对应的LCA也给记录下来)。
如果是方点,那么还需要再进行判断一下。我们要看它是可以直接选择这条边还是需要去选择环的另一边:举例如下
那么,我们要怎么去求这个x和y的LCA呢?我们在求的这个LCA点之前,我们的x和y都会达到与LCA点只差一个的u、v点,也很容易知道的是,u、v点一定是LCA环上的点(圆方树定理)。此时的LCA是方点,我们可以知道这个LCA的环的权值是多少,同样的也可以知道u、v点在LCA位置作为它的子结点,我们可以求得它两的距离(不一定是两者的直接相加、可能是相减)。
接下去,求得两者的距离之后,我们有可能不直接走这条路,会去走环的另一边去从x到达y点也是说不准的,毕竟一个环上点到“方点”的距离是固定的,但不一定是其余点的最短距离。
也就是说,我们在不断的跑Tarjan的同时,我们需要知道的是每个环的长度。
这时候又需要怎样去计算呢?
但是仙人掌的缩点可是与Tarjan的直接缩点是有区别的,从Tarjan直接缩点双联通分量来看,dfn{1, 2, 3, 4, 8, 9, 10}这几个结点的缩点之后应该是在一起的,但是仙人掌的圆方图可不是如此!
我们会把dfn{1, 2, 3, 9, 10}这几个点放在一个分支上去,把dfn{3, 4, 8, 9}这几个点放在一个分支上,再是dfn{5, 6, 7}这几个点在一起的。
那么这里的low值可得怎样处理呢?当我们搜索完这个点之后,发现它(v)的low值和自己(u)的dfn值是相等的时候,那么它们是一定构成了一个环的,此时,我们新加进去一个结点(新建一个方点++tot),然后u链接上方点,并且把现在栈stack集合中已经有的元素弹出到v作为截止(因为u点可能还是别人的u点或者是v点,先走这样的u再走v)。
这里,我们需要按照题目说的那样,找到环的长度,这时候的栈顶是什么?dfn=5的那个环的栈顶是dfn=7的那个点,也就是环末尾的那个值,那么我们是不是只用知道dfn=5和dfn=7两点确定的边的距离岂不是就可以了。——这时候用一下仙人掌的性质,每个点,如果存在下一个指向结点是已被访问过的,那么这种类型的下一个结点有且唯一。那么,我们是不是可以记录一下栈顶的下一个已被访问过的结点的距离即可求得整个环的距离了。
else if(instack[v])
{
low[u] = min(low[u], dfn[v]);
lst[u] = G.val[i]; //我用lst来处理栈顶的下一个已被访问过的结点距离关系
}
然后,讲到这里就应该差不多了吧,有什么还不理解的可以在下面进行评论。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&( -x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define efs 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e4 + 7;
int N, M, Q, tot, root[maxN][20], deep[maxN];
ll road[maxN][20] = {0};
struct Graph
{
int head[maxN], cnt, to[maxN<<2], nex[maxN<<2]; ll val[maxN<<2];
Graph()
{
memset(head, -1, sizeof(head));
cnt = 0;
}
inline void addEddge(int u, int v, ll w)
{
nex[cnt] = head[u]; to[cnt] = v; val[cnt] = w;
head[u] = cnt++;
}
inline void _add(int u, int v, ll w) { addEddge(u, v, w); addEddge(v, u, w); }
}E, G;
int dfn[maxN], low[maxN], tim, Stap[maxN], Stop;
ll dis[maxN], len[maxN], lst[maxN];
bool instack[maxN], type[maxN];
inline void work(int u, int v)
{
tot++; int tmp; len[tot] = dis[Stap[Stop]] - dis[u] + lst[Stap[Stop]];
do
{
tmp = Stap[Stop--]; instack[tmp] = false;
ll round_1 = dis[tmp] - dis[u], round_2 = len[tot] - round_1;
E._add(tmp, tot, min(round_1, round_2));
type[tmp] = (round_1 >= round_2);
}while(tmp != v);
E._add(u, tot, 0);
}
void Tarjan(int u, int fa)
{
dfn[u] = low[u] = ++tim;
Stap[++Stop] = u;
instack[u] = true;
for(int i=G.head[u], v; ~i; i=G.nex[i])
{
v = G.to[i];
if(v == fa) continue;
if(!dfn[v])
{
dis[v] = dis[u] + G.val[i];
Tarjan(v, u);
if(low[v] > dfn[u])
{
instack[Stap[Stop--]] = false;
E._add(u, v, G.val[i]);
}
else if(low[v] == dfn[u])
{
work(u, v);
}
low[u] = min(low[u], low[v]);
}
else if(instack[v])
{
low[u] = min(low[u], dfn[v]);
lst[u] = G.val[i];
}
}
}
void dfs(int u, int fa)
{
for(int i=E.head[u], v; ~i; i=E.nex[i])
{
v = E.to[i];
if(v == fa) continue;
root[v][0] = u; road[v][0] = E.val[i];
deep[v] = deep[u] + 1;
dfs(v, u);
}
}
inline void pre_LCA()
{
dfs(1, 0);
for(int j=0; (1<<j) <= N; j++)
{
for(int i=1; i<=tot; i++)
{
root[i][j + 1] = root[root[i][j]][j];
road[i][j + 1] = road[i][j] + road[root[i][j]][j];
}
}
}
inline ll LCA(int x, int y)
{
ll ans = 0;
if(deep[x] < deep[y]) swap(x, y);
int det = deep[x] - deep[y];
for(int i=log2(1. * det); i>=0; i--)
{
if(det & (1 << i))
{
ans += road[x][i];
x = root[x][i];
}
}
if(x == y) return ans;
for(int i=log2(1. * N); i>=0; i--)
{
if(root[x][i] != root[y][i])
{
ans += road[x][i] + road[y][i];
x = root[x][i];
y = root[y][i];
}
}
if(root[x][0] <= N) return ans + road[x][0] + road[y][0];
ll minn = 0, round_1 = road[x][0], round_2 = road[y][0];
if(type[x] == type[y])
{
minn = min(abs(round_1 - round_2), len[root[x][0]] - abs(round_1 - round_2));
}
else
{
minn = min(round_1 + round_2, len[root[x][0]] - (round_1 + round_2));
}
return ans + minn;
}
inline void init()
{
tim = Stop = 0; tot = N;
for(int i=1; i<=N; i++) instack[i] = false;
for(int i=1; i<=N; i++) deep[i] = 0;
}
int main()
{
scanf("%d%d%d", &N, &M, &Q);
init();
for(int i=1, u, v, w; i<=M; i++)
{
scanf("%d%d%d", &u, &v, &w);
G._add(u, v, w);
}
Tarjan(1, 0);
pre_LCA();
int u, v;
while(Q--)
{
scanf("%d%d", &u, &v);
printf("%lld\n", LCA(u, v));
}
return 0;
}