ZOJ 4097 Rescue the Princess 边双缩点+LCA
作者:互联网
给你一个图和三个点U,V,W 问你是否存在从U到V和从U到W的两条边不相交路径
先边双缩点 再每个连通分量搞LCA 最后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]; inline void read(int &v) { 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() { read(T); while (T--) { read(n), read(m), read(q); init(n + 1); for (int i = 1; i <= m; i++) { read(u), read(v); 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]) { addedge(block[i], block[v], 1); } } for (int i = 1; i < cnt; i++) { if (!vis[i]) { depth[i] = 0; root = i; dfs(root); } } for (int i = 1; i <= q; i++) { read(u), read(v), read(w); 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
标签:4097,Rescue,int,ZOJ,depth,edges,maxn,grand,void 来源: https://www.cnblogs.com/Aragaki/p/10712568.html