P5058 [ZJOI2004]嗅探器 tarjan割点

这个题是tarjan裸题。最后bfs暴力找联通块就行。(一开始完全写错了竟然得了70分,题意都理解反了。。。这数据强度。。。)

题干:

题目描述

某军搞信息对抗实战演习,红军成功地侵入了蓝军的内部网络,蓝军共有两个信息中心,红军计划在某台中间服务器上安装一个嗅探器,从而能够侦听到两个信息中心互相交换的所有信息,但是蓝军的网络相当的庞大,数据包从一个信息中心传到另一个信息中心可以不止有一条通路。现在需要你尽快地解决这个问题,应该把嗅探器安装在哪个中间服务器上才能保证所有的数据包都能被捕获?
输入输出格式
输入格式:

输入文件的第一行一个整数 n,表示蓝军网络中服务器的数目。

接下来若干行是对蓝军网络的拓扑结构描述,每行是两个整数 i , j 表示编号为 i 和编号为 j 的两台服务器间存在连接(显然连接是双向的),服务器的编号从 1 开始,一行两个 0 表示网络的拓补结构描述结束,再接下来是两个整数 a , b 分别表示两个中心服务器的编号。

输出格式:

输出编号。如果有多个解输出编号最小的一个,如果找不到任何解,输出 No solution

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(register int i = a;i <= n;++i)
#define lv(i,a,n) for(register int i = a;i >= n;--i)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1 << 30;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < ‘0‘ || c > ‘9‘)
        if(c == ‘-‘) op = 1;
    x = c - ‘0‘;
    while(c = getchar(), c >= ‘0‘ && c <= ‘9‘)
        x = x * 10 + c - ‘0‘;
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar(‘-‘), x = -x;
    if(x >= 10) write(x / 10);
    putchar(‘0‘ + x % 10);
}
const int N = 1e5 + 5;
struct node
{
    int l,r,nxt;
}a[N * 5];
int n,A,B;
int x,y;
int lst[N],len,cnt = 0,dfn[N],low[N];
int cut[N],child = 0;
void add(int x,int y)
{
//    cout<<x<<" "<<y<<endl;
    a[++len].l = x;
    a[len].r = y;
    a[len].nxt = lst[x];
    lst[x] = len;
}
void tarjan(int x,int fa)
{
    dfn[x] = low[x] = cnt++;
    for(int k = lst[x];k;k = a[k].nxt)
    {
        int y = a[k].r;
        if(!dfn[y])
        {
            tarjan(y,x);
            low[x] = min(low[x],low[y]);
            if(low[y] >= dfn[x] && x != fa)
            {
                cut[x] = 1;
            }
            if(x == fa)
            {
                child++;
            }
        }
        low[x] = min(low[x],dfn[y]);
    }
    if(child >= 2 && x == fa)
    {
        cut[x] = 1;
    }
}
bool vis[N];
bool check(int x)
{
    clean(vis);
    queue <int> q;
    vis[A] = 1;
    q.push(A);
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
//        cout<<now<<"???"<<endl;
        if(now == x) continue;
        for(int k = lst[now];k;k = a[k].nxt)
        {
            int y = a[k].r;
            if(vis[y] == 1) continue;
            if(y == x) continue;
            if(y == B) return false;
            vis[y] = 1;
            q.push(y);
        }
    }
    return true;
}
int main()
{
    read(n);
    while(1)
    {
        read(x);read(y);
        if(x == 0 && y == 0)
        {
            break;
        }
        add(x,y);
        add(y,x);
    }
    read(A);read(B);
    duke(i,1,n)
    {
        if(!dfn[i])
        {
            tarjan(i,i);
        }
    }
    duke(i,1,n)
    {
        if(i == A || i == B) continue;
        if(cut[i])
        {
//            cout<<i<<endl;
            if(check(i) == true)
            {
                printf("%d\n",i);
                return 0;
            }
        }
    }
    printf("No solution\n");
    return 0;
}

原文地址:https://www.cnblogs.com/DukeLv/p/10425290.html

时间: 2024-11-12 02:21:01

P5058 [ZJOI2004]嗅探器 tarjan割点的相关文章

题解 P5058 【[ZJOI2004]嗅探器】

Solution [ZJOI2004]嗅探器 题目大意:给定一个无向图,求一个编号最小的点\(p\),使得删掉\(p\)后\(s\)和\(t\)不连通 分析:首先我们要明确:点\(p\)一定是割点,因为只有你删掉一个点后图不连通才有可能使得\(s\)和\(t\)不连通,然后我们可以用\(tarjan\)来做这个事情 常规求割点是什么,时间戳\(dfn\),不经过树边可以到达的最小时间戳\(low\) 如果存在\(u\)以及它的子节点\(v\),如果有\(dfn[u] \leq low[v]\)那

Luogu5058 [ZJOI2004]嗅探器

\(\verb|Luogu5058 [ZJOI2004]嗅探器|\) 给定一张 \(n\) 个点, \(m\) 条边的无向图,和两点 \(s,\ t\) ,求 \(s\to t\) 编号最小的必经点(排除 \(s,\ t\) ) \(n\leq100\) tarjan 这题数据范围是可以 \(O(n^3)\) 暴力过的-- 显然只需缩点后的树上 \(bl_s\) 到 \(bl_t\) 上找答案,统计割点贡献即可 然而此题有更简单的做法-- 从 \(s\) 开始 tarjan,点 \(u\) 对答

[zjoi2004]嗅探器

某军搞信息对抗实战演习.红军成功地侵入了蓝军的内部网络. 蓝军共有两个信息中心.红军计划在某台中间服务器上安装一个嗅探器,从而能够侦听到两个信息中心互相交换的所有信息.但是蓝军的网络相当的庞大,数据包从一个信息中心传到另一个信息中心可以不止有一条通路.现在需要你尽快地解决这个问题.应该把嗅探器安装在哪个中间服务器上才能保证所有的数据包都能被捕获? 题解:tarjan的简单应用: #include<iostream> #include<cstdio> #include<cstd

【模板】 Tarjan割点

题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,m 下面m行每行输入x,y表示x到y有一条边 输出格式: 第一行输出割点个数 第二行按照节点编号从小到大输出节点,用空格隔开 输入输出样例 输入样例#1: 6 7 1 2 1 3 1 4 2 5 3 5 4 5 5 6 输出样例#1: 1 5 说明 n,m均为100000 tarjan 图不一定联通!!! 1 #include<iostream> 2 #include<cstdio> 3

Tarjan割点

$Tarjan$求割点 感觉图论是个好神奇的东西啊,有各种奇奇怪怪的算法,而且非常巧妙. 周末之前说好回来之后进行一下学术交流,于是wzx就教给我Tarjan,在这里我一定要说: $wzx AK IOI$ Tarjan发明了很多算法,而且还都叫一个名字,所以说只好用用途来区分它们. 闲聊时间结束. 首先,什么是割点呢?在一个无向图中,如果有一个顶点,删除这个顶点以及所有相关联的边以后,图的连通分量增多,就称这个点为割点. 首先找一个点作为根进行搜索,把图按照$dfs$的方法组织成一棵搜索树,树上

Tarjan&amp;割点&amp;割边&amp;点双&amp;边双&amp;缩点

文末有福利. Tarjan是通过搜索树和压栈完成的,维护两个东西:dfn[i](时间戳).low[i](通过搜索树外的边i(返祖边),节点能到达的最小节点的时间戳). 跑完Tarjan,缩点,可以得到DAG图(有向无环图),可以再建图或统计入度出度. 在有向图中,可以找强连通分量SCC(极大强联通子图)(任意两点可以互达): 多维护一个vis[i]表示在不在栈中. 1 void tarjan_(int u) 2 { 3 stack[++tp]=u; 4 dfn[u]=low[u]=++num;

tarjan割点算法代码实现

#include<iostream> using namespace std; int n,m,x,y; int e[9][9]; int root=1; int timex;//时间戳 int num[9],low[9],flag[9];//flag标记割点 int min(int a,int b){ if(a<b){ return a; }else{ return b; } } void dfs(int cur,int father){ int child=0; timex++; n

Tarjan 割点割边【模板】

1 #include <algorithm> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 const int N(100000+15); 8 int n,m,u,v; 9 int head[N],sumedge; 10 struct Edge 11 { 12 int to,next; 13 Edge(int to=0,int next=0) : 14 to(to),next(

Tarjan-求割点

知识点-Tarjan 割点:在一个无向连通图中,如果删掉点 x 后图的连通块数量增加,则称点 为图的割点. 条件: 1)对于搜索树上的非根结点 x ,如果存在子节点 i 满足 F[i]>=D[x]  ,即 i 向上无法达到 x 的祖先,则 x 为割点,这一点比较能够理解. 2)对于搜索树上的根节点x,若它的子节点数 >=2 ,则 x 为割点.说明 i 必须通过 x 节点到达 x 的祖先,这样去掉 x 后,就能分出两个强连通块. 例题 输入 第一行两个整数,n,m,代表点数及边数. 第2行至m+