hdu 3686 点双连通 按<割点>缩点 + 倍增lca

题意:给你一幅图,然后询问 从边x到边y要经过几个一定要经过的点。

分析:显然,这些点一定是割点。问题就变成 按割点缩点的点双联通   的新图 里面 从 边x所属于的点  到 边y所属于的点  要经过几个割点。

注意:如何缩点?把每个联通分量看成一个点(a类点),把所有割点再看成一个点(b类点),然后新图一定是   a类点连b类点,b类点连a类点, 同一类的点不会直接相连,所以我们要求点x 到 点y 经过的割点(x,y一定是a类点),那么它们之间的路径所经过的点数和的一半就是它们之间所经过的割点的个数。

//*************点双连通    按<割边>缩点
//所有的点编号从1开始
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 10004 << 1;
const int maxm = 100005 << 1;
struct Edge {
	int u, v, vis, next;
	Edge(){}
	Edge(int u, int v, int next):u(u), v(v), next(next), vis(0) {}
} edge[maxm];
int head[maxn], E;
void init() {
	memset(head, -1, sizeof(head));
	E = 0;
}
void add(int s, int t) {
	edge[E] = Edge(s, t, head[s]);
	head[s] = E++;
	edge[E] = Edge(t, s, head[t]);
	head[t] = E++;
}
int n, m;
int dfn[maxn], low[maxn];
int belo[maxm], ctot;  //belo:边属于哪个联通分量  ctot:联通分量的个数
int st[maxm], top, time;
int cut[maxn], net[maxn];
//cut:点i是否为割点, net:去掉割点i后能增加的联通分量的个数
vector<int> node[maxn];  //节点i所在的联通分量标号
vector<int> com[maxn];   //联通分量i中所有点
void color(int u) {
	int s, t, k;
	com[ctot].clear();
	ctot++;
	do {
		k = st[top--];
		s = edge[k].u;
		t = edge[k].v;
		node[s].push_back(ctot);
		node[t].push_back(ctot);
		com[ctot].push_back(s);
		com[ctot].push_back(t);
		belo[(k>>1)+1] = ctot + n;
	} while (s != u);
}
void dfs(int u) {
	low[u] = dfn[u] = ++time;
	int i, v;
	for(i = head[u]; ~i; i = edge[i].next) {
		if (edge[i].vis)
			continue;
		edge[i].vis = edge[i^1].vis = 1;
		v = edge[i].v;
		st[++top] = i;
		if (!dfn[v]) {
			dfs(v);
			low[u] = min(low[u], low[v]);
			if (low[v] >= dfn[u]) {
				net[u]++;
				cut[u] = 1;
				color(u);
			}
		} else
			low[u] = min(low[u], dfn[v]);
	}
}
vector<int> nedge[maxn]; //缩点后的图
void bcc(int n) {
	int i, j;
	ctot = time = top = 0;
	for(i = 0; i <= n; i++) {
		node[i].clear();
		dfn[i] = net[i] = cut[i] = 0;
	}
	for(i = 1; i <= n; i++)if(!dfn[i]) {
		dfs(i);
		net[i]--;
		if(net[i] <= 0) cut[i] = 0;
	}
	for(i = 0; i <= ctot + n; i++)
		nedge[i].clear();
	int a, b;
	for(i = 1; i <= n; i++)
		if (cut[i]) {
			for (j = 0; j < node[i].size(); j++) {
				a = i;
				b = node[i][j] + n;
				nedge[a].push_back(b);
				nedge[b].push_back(a);
			}
		}
}

const int Pow = 18;
int d[maxn], p[maxn][Pow];
bool vis[maxn];
void dfs(int u, int fa) {
	int i;
	d[u] = d[fa] + 1;
	p[u][0] = fa;
	vis[u] = 1;
	for(i = 1; i < Pow; i++)
		p[u][i] = p[p[u][i - 1]][i - 1];
	for(i = 0; i < nedge[u].size(); i++) {
		int v = nedge[u][i];
		if(vis[v])
			continue;
		dfs(v, u);
	}
}
void lca_init(int n) {
	int i;
	for(i = 0; i <= n; i++)
		vis[i] = 0;
	for(i = 1; i <= n; i++)
		if(!vis[i])
			dfs(i, 0);
}
int lca(int a, int b) {
	if(d[a] > d[b])
		a ^= b, b ^= a, a ^= b;
	if(d[a] < d[b]) {
		int k = d[b] - d[a];
		for(int i = 0; i < Pow; i++)
			if((k >> i) & 1)
				b = p[b][i];
	}
	if(a != b) {
		for(int i = Pow - 1; i >= 0; i--)
			if(p[a][i] != p[b][i])
				a = p[a][i], b = p[b][i];
		a = p[a][0], b = p[b][0];
	}
	return a;
}
int main() {
	int i, j;
	while(~scanf("%d%d", &n, &m)) {
		if (!n && !m)
			break;
		init();
		while (m--) {
			int x, y;
			scanf("%d%d", &x, &y);
			add(x, y);
		}
		bcc(n);
		lca_init(ctot + n);
		scanf("%d", &m);
		while (m--) {
			int x, y;
			scanf("%d%d", &x, &y);
			x = belo[x], y = belo[y];
			int t = lca(x, y);
			printf("%d\n", (d[x] + d[y] - d[t] * 2) >> 1);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值