CF1045C Hyperspace Highways(圆方树板子)

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;
}
posted @ 2020-11-27 18:45  lahlah  阅读(25)  评论(0编辑  收藏  举报