CF1045C Hyperspace Highways(圆方树板子)
题目大意
给一张
n
n
n个点
m
m
m条边的图,保证若有一个环,一定是完全子图,多次询两个点之间的最短路径长度。
题解
如果会圆方树的可以一眼看出来答案就是圆方树上距离
/
2
/2
/2
因为两个点之间一定隔了一个方点
为什么可以这么转换?
挺显然的吧,因为如果是从完全子图一端到另一端,必然要经过一条边,然而这刚好对应圆方树上方点的两条边,一个点也是完全子图,然后就没了。
什么你不会建圆方树???
要建圆方树首先要会点双连通分量
点双连通分量: 图中任意两不同点之间都有至少两条点不重复的路径。
还有一个好理解很多的定义:不存在割点的图
两个点一条边的情况是特殊情况,这里不过多讨论点双
然后对于原图的每一个点对应一个圆点,每个点双对应一个方点
一个点双里的圆点全部连向那个方点
形成一个菊花子图
(图选自WC课件)
显然每条边都会连向一个圆点和一个方点
那么如何建圆方树呢???
像tarjan一样搞就行了
具体看代码吧
code:
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
struct edge {
int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
int n, m, low[N], dfn[N], tot, sta[N], sz, bel[N], insta[N], top, dep[N];
vector<int> g[N];
void add(int u, int v) {
g[u].push_back(v), g[v].push_back(u);
}
void dfs(int u) {
low[u] = dfn[u] = ++ tot;
sta[++ top] = u; insta[u] = 1;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(!dfn[v]) {
dfs(v), low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) { //如果low[v]大于dfn[u],说明断掉u这个点后下面的点和上面不连通,这是割点,所以下面是个点双
++ sz;
add(u, sz);//注意要把 u 也和 方点连边
while(sta[top] != v) add(sta[top], sz), bel[sta[top]] = sz, insta[sta[top --]] = 0;
add(sta[top], sz), bel[sta[top]] = sz, insta[sta[top --]] = 0;//把点双里一直到 v 的点全部连向方点 ,因为 u 可能属于多个点双,所以不弹出 u
}
}
else low[u] = min(low[u], dfn[v]);//像tarjan一样求low
}
}
int fa[N][22], q;
void dfss(int u) {
for(int i = 0; i < g[u].size(); i ++) {
int v = g[u][i];
if(v == fa[u][0]) continue;
dep[v] = dep[u] + 1;
fa[v][0] = u;
dfss(v);
}
}
int LCA(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = 19; i >= 0; i --) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if(x == y) return x;
for(int i = 19; i >= 0; i --) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}//求两点距离用的
int main() {
init();
scanf("%d%d%d", &n, &m, &q);
sz = n;
for(int i = 1; i <= m; i ++) {
int u, v;
scanf("%d%d", &u, &v);
insert(u, v);
insert(v, u);
}
for(int i = 1; i <= n; i ++) if(!dfn[i]) dfs(i), dfss(i);
for(int j = 1; j <= 19; j ++)
for(int i = 1; i <= sz; i ++)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
while(q --) {
int x, y;
scanf("%d%d", &x, &y);
int lca = LCA(x, y);
printf("%d\n", (dep[x] + dep[y] - 2 * dep[lca]) >> 1);//答案就是两点距离 / 2
}
return 0;
}