zoj 4097 Rescue the Princess (缩点+LCA)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4097

题意

在一个无向图中,询问u,v,w, v和w是否存在边不相同的路径到u。

题解

点双连通分量:任意两点之间存在两条点互不相同的路径。
边双连通分量:任意两点之间存在两条边互不相同的路径。

点双连通分量也是边双连通分量。
但边双连通分量不是点双连通分量。

一般这种题都需要缩点变树。
用边双连通分量进行缩点,这样只剩下桥,即变成了一颗树,因为两点之间有且只有一条路径可达。

分类讨论:

  1. v和w在一个连通分量内,如果u也在输出Yes,否则No。
  2. v和w不在一个连通分量内,那么u必须在v到w的路径之上。

对于条件2,用LCA来判断

  1. u必须是v的父亲或者是w的父亲。
  2. 这个父亲不能超过v和w的公共祖先。 即要是LCA(v,w)的儿子。

用边双连通分量缩点要注意桥是双向的,加边的话只要取出一个桥加边即可,不然会加两倍的重复边。

代码

#include <bits/stdc++.h>
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int,int> pii;

const int maxn = 1e5+5;
const int maxm = 2e5+5;
vector<int> G[maxn];
vector<pii> edges;
vector<int> G2[maxn];
int n,m,q, dfn[maxn], low[maxn], belong[maxn], isbridge[2*maxm];
int conn[maxn];
int bcc_cnt, dfn_tim;
inline int read()
{
    int k=0;
    char f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar() )
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar() )
        k=k*10+c-'0';
    return k*f;
}
int find(int x) {
    return x == conn[x] ? x : conn[x] = find(conn[x]);
}
void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++dfn_tim;
    // cout << u <<" "<< dfn[u] << endl;
    for(int i = 0; i < G[u].size(); ++i) {
        int v = edges[G[u][i]].second;
        if(!dfn[v]) {
            tarjan(v,u);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u]) {
                isbridge[G[u][i]] = isbridge[G[u][i]^1] = 1;
            }
        } else if(dfn[v] < dfn[u] && v != fa) {
            low[u] = min(low[u], dfn[v]);
        }
    }
}
void dfs(int u) {
    dfn[u] = 1;
    belong[u] = bcc_cnt;
    for(int i = 0; i < G[u].size(); ++i) {
        int e = G[u][i];
        int v = edges[e].second;
        if(!isbridge[e] && !dfn[v]) {
            dfs(v);
        }
    }
}
void find_ebcc() {
    dfn_tim = bcc_cnt = 0;
    memset(dfn, 0, sizeof dfn);
    memset(low, 0, sizeof low);
    memset(isbridge,0,sizeof isbridge);
    for(int i = 1; i <= n; ++i) {
        if(!dfn[i])
            tarjan(i,-1);
    }
    memset(dfn, 0, sizeof dfn);
    for(int i = 1; i <= n; ++i) {
        if(!dfn[i]) {
            bcc_cnt++;
            dfs(i);
        }
    }

}
void add(int u, int v) {
    edges.push_back(pii(u,v));
    edges.push_back(pii(v,u));
    G[u].push_back(edges.size()-2);
    G[v].push_back(edges.size()-1);
}
int p[maxn][18], dep[maxn];

void dfs2(int u, int fa) {
    for(int i = 1; i <= 17; ++i) {
        p[u][i] = p[p[u][i-1]][i-1];
    }
    for(int i = 0; i < G2[u].size(); ++i) {
        int v = G2[u][i];
        if(v != fa) {
            dep[v] = dep[u]+1;
            p[v][0] = u;
            dfs2(v,u);
        }
    }
}
int lca(int x, int y) {
    if(dep[x] < dep[y])
        swap(x,y);
    for(int i = 17; i >= 0; --i) {
        if(dep[p[x][i]] >= dep[y])
            x = p[x][i];
    }
    
    if(x == y) return x;
    for(int i = 17; i >= 0; --i) {
        if(p[x][i] != p[y][i]) {
            x = p[x][i];
            y = p[y][i];
        }
    }

    return p[x][0];
}
void solve() {
    find_ebcc();

    for(int i = 0; i < edges.size(); i += 2) {
        int u = edges[i].first, v = edges[i].second;
        if(isbridge[i]) {
            G2[belong[u]].push_back(belong[v]);
            G2[belong[v]].push_back(belong[u]);
        }
        u = find(u); v = find(v);
        if(u != v)
            conn[u] = v;
    }
    for(int i = 1; i <= bcc_cnt; ++i) {
        if(!dep[i]) {
            dep[i] = 1;
            dfs2(i,-1);
        }       
    }
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d%d", &n, &m, &q);
        memset(p,0,sizeof p);
        memset(dep,0,sizeof dep);
        for(int i = 1; i <= n; ++i) {
            p[i][0] = 0;
            G[i].clear();
            G2[i].clear();
            conn[i] = i;
        }
        edges.clear();
        int u,v;
        for(int i = 0; i < m; ++i) {
            u = read(); v = read();
            add(u,v);
        }
        solve();
        
        for(int i = 0; i < q; ++i) {
            int u,v,w;
            u = read(); v = read(); w = read();
            // scanf("%d%d%d", &u, &v, &w);
            if(find(u) != find(v) || find(w) != find(u)) {
                puts("No");
                continue;
            }
            // u 不在 v-w的路径上
            u = belong[u]; v = belong[v]; w = belong[w];

            int uv = lca(u,v);
            int vw = lca(v,w);
            int uw = lca(u,w);
            int uvw = lca(u,vw);
            if(v == w) {
                if(u == v) puts("Yes");
                else puts("No");
            } else {
                if(uvw == vw && (u == uv || u == uw)) puts("Yes");
                else puts("No");
            }
            
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值