## ZOJ 4097 Rescue the Princess 边双缩点+LCA

```#include<bits/stdc++.h>
using namespace std;
#define INF 0xfffffff
#define maxn 200025
#define min(a,b) (a<b?a:b)
int m, n, Time, cnt, top;
int dfn[maxn], block[maxn], low[maxn], Father[maxn], Stack[maxn];
int Bcc[maxn], Bcccnt = 0;
vector<int> G[maxn], G2[maxn];
v = 0;
char c = 0;
int p = 1;
while (c < '0' || c > '9') {
if (c == '-') {
p = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9') {
v = (v << 3) + (v << 1) + c - '0';
c = getchar();
}
v *= p;
}
void Tarjan(int u, int fa) {
dfn[u] = low[u] = ++Time;
Father[u] = fa;
Stack[top++] = u;
int len = G[u].size(), v, k = 0;
for (int i = 0; i < len; i++) {
v = G[u][i];
if (v == fa && !k) {
k ++;
continue;
}
if (!low[v]) {
Tarjan(v, u);
low[u] = min(low[u], low[v]);
} else {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
do {
v = Stack[--top];
block[v] = cnt;
} while (u != v);
cnt ++;
}
}
void getBcc(int x, int y) {
Bcc[x] = y;
for (auto v : G[x]) {
if (Bcc[v] == 0) {
getBcc(v, y);
}
}
}
int T;
int q, N;
int u, v, c, w;

typedef struct {
int from, to, w;
} edge; //这个结构体用来存储边
vector<edge> edges;
//保存边的数组
int grand[maxn][20];  //x向上跳2^i次方的节点，x到他上面祖先2^i次方的距离
int depth[maxn];//深度
int root;
bool vis[maxn];
void addedge(int x, int y, int w) { //把边保存起来的函数
edge a = {x, y, w}, b = {y, x, w};
edges.push_back(a);
edges.push_back(b);
G2[x].push_back(edges.size() - 2);
G2[y].push_back(edges.size() - 1);
}
void dfs(int x) { //dfs建图
vis[x] = 1;
for (int i = 1; i <= N; i++) { //第一个几点就全部都是0,第二个节点就有变化了,不理解的话建议复制代码输出下这些数组
grand[x][i] = grand[grand[x][i - 1]][i - 1];  //倍增 2^i=2^(i-1)+2^(i-1)
}
for (int i = 0; i < G2[x].size(); i++) {
edge  e = edges[G2[x][i]];
if (e.to != grand[x][0]) { //这里我们保存的是双向边所以与他相连的边不是他父亲就是他儿子父亲的话就不能执行，不然就死循环了。
depth[e.to] = depth[x] + 1; //他儿子的深度等于他爸爸的加1
grand[e.to][0] = x; //与x相连那个节点的父亲等于x
//gwmax[e.to][0]=e.w;
dfs(e.to);//深搜往下面建
}
}
}
int lca(int a, int b) {
if (a == b) {
return a;
}
if (depth[a] > depth[b]) {
swap(a, b);        //保证a在b上面，便于计算
}
for (int i = N; i >= 0; i--) { //类似于二进制拆分，从大到小尝试
if (depth[a] < depth[b] && depth[grand[b][i]] >= depth[a]) { //a在b下面且b向上跳后不会到a上面
b = grand[b][i];        //先把深度较大的b往上跳
}
}
if (a == b) {
return a;
}
for (int j = N; j >= 0; j--) { //在同一高度了,他们一起向上跳,跳他们不相同节点，当全都跳完之后grand【a】【0】就是lca,上面有解释哈。
if (grand[a][j] != grand[b][j]) {
a = grand[a][j];
b = grand[b][j];
}
}
if (grand[a][0] == 0 && grand[b][0] == 0 && a != b) {
return -1;
}
return grand[a][0];
}

void init(int n) {
edges.clear();
Bcccnt = cnt = 1;
top = Time = 0;
for (int i = 0; i <= n; i++) {
vis[i] = dfn[i] = low[i] = block[i] = Father[i] = Bcc[i] = 0;
G[i].clear();
G2[i].clear();
}
}
int main() {
while (T--) {
init(n + 1);
for (int i = 1; i <= m; i++) {
if (u != v) {
G[u].push_back(v);
G[v].push_back(u);
}
}
for (int i = 1; i <= n; i++) {
if (Bcc[i] == 0) {
getBcc(i, Bcccnt);
Bcccnt++;
}
}
for (int i = 1; i <= n; i++) {
if (!low[i]) {
Tarjan(i, i);
}
}
depth[0] = -1;
N = floor(log(cnt + 0.0) / log(2.0)) + 1; //最多能跳的2^i祖先
for (int i = 1; i <= n; i++) {
v = Father[i];
if (block[i] != block[v]) {
}
}
for (int i = 1; i < cnt; i++) {
if (!vis[i]) {
depth[i] = 0;
root = i;
dfs(root);
}
}
for (int i = 1; i <= q; i++) {
if (Bcc[u] != Bcc[v] || Bcc[u] != Bcc[w]) {
printf("No\n");
continue;
}
u = block[u], v = block[v], w = block[w];
if (u == v || u == w) {
printf("Yes\n");
continue;
}
if (v == w) {
printf("No\n");
continue;
}
int t[] = {u, lca(u, w), lca(u, v), lca(v, w)};
sort(t, t + 4, [](int x, int y) {
return depth[x] < depth[y];
});
if (t[2] == u && t[3] == u) {
printf("Yes\n");
} else {
printf("No\n");
}
}
}
return 0;
}```
View Code

0条评论