您的位置:首页 > 其它

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

2013-10-07 13:37 225 查看
题意:给你一幅图,然后询问 从边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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  联通