P5058 [ZJOI2004] 嗅探器 题解

显然,答案的点一定是割点,找割点我们可以用Tarjan算法,但是割点不一定是答案点。我们不妨从\(dfn\)序的角度出发,如果是答案点的话,假设\(u\)是割点,\(u-v\)是一条割边,一定是有\(dfn[v] \le dfn[b]\)的。这表明\(b\)是遍历\(v\)的子树的时候遍历到的。如果\(dfn[v] > dfn[b]\),则有两种情况,第一种情况是在遍历\(v\)之前就已经访问过了\(b\),那么\(v\)这个点一定无法阻断所有的走向\(b\)的通路。第二种情况是\(dfn[b]=0\),那么这个时候压根没访问到\(b\)\(v\)这个点自然也不是答案。

代码

#include <bits/stdc++.h>
using namespace std;

const int N=2e5+10;
int dfn[N],low[N],idx;
int n,a,b;
vector<int> e[N];
bool flag[N];

void Tarjan(int u,int fa){
    dfn[u]=low[u]=++idx;
    for(int v:e[u]){
        if(!dfn[v]){
            Tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(u!=fa&&low[v]>=dfn[u]&&!flag[u]&&dfn[v]<=dfn[b]){
                flag[u]=true;
            }
        }else if(v^fa){
            low[u]=min(low[u],dfn[v]);
        }
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);

    cin>>n;
    while(1){
        int u,v;
        cin>>u>>v;
        if(u==0&&v==0) break;
        else{
            e[u].push_back(v),e[v].push_back(u);
        }
    }
    cin>>a>>b;
    Tarjan(a,a);    
    for(int i=1;i<=n;i++){
        if(flag[i]){
            cout<<i;
            return 0;
        }
    }
    cout<<"No solution";
    return 0;
}

总结:处理割点有关问题的时候,可以从\(dfn\)序的角度出发进行考虑。

posted @ 2026-03-12 21:33  1445141  阅读(24)  评论(0)    收藏  举报