Luckyleaves's Blog

stay hungry,stay foolish greedy and lucky

仙人掌

一种不怎么常用的算法(闲的没事,随便看看)

简介

什么是仙人掌,简单来讲;

一个只存在简单环,而且不存在任何一条边属于两个环的无向联通图是仙人掌

考虑怎么将这个复杂的图经行转换,然后变成简单的图,经过几轮手玩之后,不难发现每一次图上的游走的路径都会经行如下拆分:

  • 必须走的边
  • 环的一部分

而且在大部分的情况下通过环的最优情况是一定的,所以我们可以将环进行如下改造:

image

image

可以发现,现在的路径被唯一确定了,那么整个图就变成了一颗树,叫做圆方树,现在思考我们怎么样将每个简单环给找出来,我们找环一般都是靠tarjan,但是tarjan求得是边双联通分量,当两个环连在一起的,这两个环就会被缩在一起,所以需要对该算法经行改造,观察原来的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void tarjan(int x, int from)
{
dfn[x] = low[x] = ++ tot;
st[++ top] = x, ins[x] = 1;
for(int i = h[x]; ~i;i = ne[i])
{
if(i == (from ^ 1)) continue;
if (!dfn[e[i]])
{
tarjan(e[i], i);
low[x] = min(low[x], low[e[i]]);
}
else if (ins[e[i]]) low[x] = min(low[x], dfn[e[i]]);
}
if (low[x] != dfn[x]) return;
cnt ++;
int now;
do{
now = st[top --];
ins[now] = 0;
bel[now] = cnt;
}while (now != x);
}

(每个人板子可能不一样)

发现问题主要出在下面将st里面的数字拿出来时,我们将栈中的所有点都归为一个环,但此时我们应该去找到简单环,所以当我们遇到$low[e[i]] < dfn[x]$的时候就必须倒回去找环,对此我们记录每一个点的来向,倒着找回去即可。

紧接着考虑我们线在建出圆方树之后对圆方树的边权我们应该怎样确定,我们来看一个简单环:

image

为了保证在原图上最小路径的权值等价于新树上路径的权值,我们在遍历整个环的时候令最先遍历到的点为根,树上圆点到方点的距离就是环上圆点到根的距离。

例题:P5236 【模板】静态仙人掌

考虑本题,我们只需求$x,y$两点在圆方树上的lca,如果lca是圆点,那么直接输出树上距离,如果是方点,$x,y$到lca下面两点的距离,然后在求出这两点在环上的距离即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <bits/stdc++.h>
#define LL long long
#define PII pair<LL, LL>
using namespace std;
template <class T>
inline void read(T &res)
{
res = 0;
bool flag = 0;
char c = getchar();
while (c < '0' || '9' < c) { if (c == '-') flag = 1; c = getchar(); }
while ('0' <= c && c <= '9') res = (res << 3) + (res << 1) + (c ^ 48), c = getchar();
if (flag) res = -res;
}
template <class T, class... ARC>
inline void read(T &res, ARC &...com) { read(res), read(com...); }
template <class T>
void write(T res)
{
if (res < 0) putchar('-'), res = -res;
if (res > 9) write(res / 10);
putchar(res % 10 + '0');
}
template <>
inline void write(char c) { putchar(c); }
template <>
inline void write(char *s) { while (*s) putchar(*s++); }
template <class T, class... ARC>
inline void write(T res, ARC... com) { write(res), write(com...); }
const int N = 2e4 + 5, M = 1e5 + 5;
int n, m, T;
int idx = 1;
int e[M << 1], ne[M << 1], h1[N], h2[N], w[M << 1];
inline void add(int h[], int x, int y, int z)
{
idx ++;
e[idx] = y, ne[idx] = h[x], h[x] = idx, w[idx] = z;
}
int dfn[N], low[N], tot, cnt;
int stot[N], s[N], fa[N], fw[N], A, B;
inline void build(int x, int y, int w)
{
int sum = w;
for(int i = y;i != x;i = fa[i])
{
s[i] = sum;
sum += fw[i];
}
stot[x] = s[x] = sum;
add(h2, x, ++ cnt, 0);
for(int i = y;i != x;i = fa[i])
{
stot[i] = sum;
add(h2, cnt, i, min(s[i], stot[i] - s[i]));
}
}
void tarjan(int x, int from)
{
dfn[x] = low[x] = ++ tot;
for(int i = h1[x], j; ~i;i = ne[i])
{
j = e[i];
if(!dfn[j])
{
fa[j] = x, fw[j] = w[i];
tarjan(j, i);
low[x] = min(low[x], low[j]);
if(low[j] > dfn[x]) add(h2, x, j, w[i]);
}
else if(i != (from ^ 1)) low[x] = min(low[x], dfn[e[i]]);
}
for(int i = h1[x], j; ~i;i = ne[i])
{
j = e[i];
if(dfn[j] > dfn[x] && fa[j] != x)
build(x, j, w[i]);
}
}
int f[N][15], depth[N], dis[N];
void dfs(int x, int y)
{
depth[x] = depth[y] + 1;
f[x][0] = y;
for(int i = 1;i <= 14;i ++)
f[x][i] = f[f[x][i - 1]][i - 1];
for(int i = h2[x], j; ~i;i = ne[i])
{
j = e[i];
if(j == y) continue;
dis[j] = dis[x] + w[i], dfs(j, x);
}
}
inline int lca(int x, int y)
{
if(depth[x] < depth[y]) swap(x, y);
for(int i = 14;i >= 0;i --)
if(depth[f[x][i]] >= depth[y]) x = f[x][i];
if(x == y) return y;
for(int i = 14;i >= 0;i --)
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
A = x, B = y;
return f[x][0];
}
inline void work()
{
int x, y, z;
read(x, y);
z = lca(x, y);
if(z <= n) write(dis[x] + dis[y] - 2 * dis[z], '\n');
else{
int res = dis[x] - dis[A] + dis[y] - dis[B];
res += min(abs(s[A] - s[B]), stot[A] - abs(s[A] - s[B]));
write(res, '\n');
}
}
int main()
{
memset(h1, -1, sizeof(h1));
memset(h2, -1, sizeof(h2));
read(n, m, T);
for(int i = 1, o, u, p;i <= m;i ++)
read(o, u, p), add(h1, o, u, p), add(h1, u, o, p);
cnt = n;
tarjan(1, -1);
dfs(1, 0);
while(T --) work();
return 0;
}