P5058 [ZJOI2004] v-DCC

该博客探讨了一种图论问题——嗅探器问题,其中涉及了寻找信息中心间的最短路径并确定是否存在联通路径。文章通过建立图模型,使用深度优先搜索(DFS)进行点双连通分量的求解,并介绍了如何通过缩点简化问题,最终找到最小割点以解决信息中心间的连通性。此外,还提到了对于小规模问题的直接枚举解法。
摘要由CSDN通过智能技术生成
题意

传送门 P5058 [ZJOI2004]嗅探器

题解

若两个信息中心位于不同的联通分量,则无解;反之,答案为信心中心间编号最小的割点。求点双连通分量,缩点后得到一棵树,用信息中心间的路径上的割点更新答案即可。

n n n 较小时,也可以简单地不通过缩点求解。枚举割点,判断信息中心是否处于不同的连通分量即可。

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
const int MAXN = 2e5 + 5;
int N, idx[MAXN];
vector<int> G[MAXN], nG[MAXN * 2];
int dep[MAXN * 2], par[MAXN * 2];
int low[MAXN], stk[MAXN], top, rt, dcc_num;
vector<int> dcc[MAXN];
bool cut[MAXN];
set<int> vs;

void dfs(int u, int p, int d)
{
    stk[top++] = u;
    dep[u] = low[u] = d, vs.insert(u);
    int s = 0;
    for (int v : G[u])
    {
        if (dep[v] == -1)
        {
            dfs(v, u, d + 1);
            low[u] = min(low[u], low[v]);
            if (dep[u] <= low[v])
            {
                ++s;
                if (s > 1 || u != rt)
                    cut[u] = 1;
                dcc[dcc_num].clear();
                int w;
                do
                {
                    w = stk[--top];
                    dcc[dcc_num].pb(w);
                } while (w != v);
                dcc[dcc_num].pb(u);
                ++dcc_num;
            }
        }
        else if (v != p)
        {
            low[u] = min(low[u], dep[v]);
        }
    }
}

void ndfs(int u, int p, int d)
{
    par[u] = p, dep[u] = d;
    for (int v : nG[u])
    {
        if (v != p)
            ndfs(v, u, d + 1);
    }
}

void upd(int &res, int u)
{
    if (u < dcc_num)
        return;
    u -= dcc_num;
    if (res == -1 || res > u)
        res = u;
}

int get(int u, int v)
{
    int res = -1;
    if (dep[u] < dep[v])
        swap(u, v);
    while (dep[u] > dep[v])
    {
        u = par[u];
        if (u != v)
            upd(res, u);
    }
    if (u == v)
        return res;
    while (u != v)
    {
        u = par[u], v = par[v];
        upd(res, u), upd(res, v);
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> N;
    for (int u, v;;)
    {
        cin >> u >> v;
        if (u == 0 && v == 0)
            break;
        --u, --v;
        G[u].pb(v), G[v].pb(u);
    }
    int s, t;
    cin >> s >> t;
    --s, --t;
    int res = -1;
    memset(dep, -1, sizeof(dep));
    memset(idx, -1, sizeof(idx));
    for (int i = 0; i < N; ++i)
    {
        if (dep[i] != -1)
            continue;
        vs.clear();
        top = 0, rt = i;
        dcc_num = 0;
        dfs(i, -1, 0);
        if (!vs.count(s) || !vs.count(t))
            continue;
        for (int j = 0; j < dcc_num; ++j)
        {
            for (int u : dcc[j])
            {
                if (cut[u])
                    nG[j].pb(dcc_num + u), nG[dcc_num + u].pb(j);
                else
                    idx[u] = j;
            }
        }
        int u = cut[s] ? s + dcc_num : idx[s];
        int v = cut[t] ? t + dcc_num : idx[t];
        ndfs(0, -1, 0);
        res = get(u, v);
        break;
    }
    if (res == -1)
        cout << "No solution\n";
    else
        cout << res + 1 << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值