首页 > 其他分享 >POJ 3694 Network

POJ 3694 Network

时间:2023-07-25 09:14:39浏览次数:50  
标签:缩点 bridge Network int 3694 割边 dfn POJ low

POJ 3694 Network

一、题目大意

\(n\)个点,\(m\)个边,连通图。

点与点之间通过边连接,如果切断某个边使得有点与其他点连接断开(连通分支增加),则称这种边为 桥梁(离散上叫 割边)。
接下来有\(Q\)个操作,每操作一次,也就是切断某条边后,输出当前存在的桥梁数量

二、样例分析

我们看这个

4 4
1 2
2 1
2 3
1 4
2
1 2
3 4
0 0

一开始是图中蓝色部分,其中\(1\)和\(4\)之间的边,\(2\)和\(3\)之间的边称之为 ,再加入\(1\sim 2\)边后(绿色),桥还是那俩没变,再加入\(4\sim 3\)边后,桥的数量为\(0\)(因为此时你去掉任何一个边,任意两个点都可连接)

二、解题思路

首先运行一次\(Tarjan\),求出桥和缩点,那么原无向图将缩点为 一棵树,树边正好是原来的桥。

每次连接两点,看看这两点是不是在同一个缩点(边双)内:

  • 如果是,那么缩点后的树没任何变化
  • 如果两点属于不同的缩点,那么连接起来,然后找这两个缩点的\(LCA\),因为从点\(u\)到\(LCA\)再到点\(v\)再到点\(u\),将形成环,里面的树边都会变成不是桥。

计数的时候注意,有些树边可能之前已经被标记了,这次再经过不能再标记

\(Code\)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int M = 400010;
const int N = 100010;

int bridge[N]; // 某个节点的入边是不是桥,比如 u->v是桥,则记录bridge[v]=1
int p[N];      // 并查集
int bcnt;      // 桥的数量

int n, m;
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

// Tarjan算法求割边模板
int dfn[N], low[N], ts;
void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++ts;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (v == fa) continue;

        if (!dfn[v]) {
            p[v] = u;     // 记录v的父节点是u
            tarjan(v, u); // 先搜索子树v
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u]) { // 发现割边
                bcnt++;            // 割边数量 +1
                bridge[v] = 1;     // 因为经Tarjan算法缩点后,是一棵树,所以,bridge[v]代表的就是父节点向v的边,也就是u->v的边是割边
            }
        } else
            low[u] = min(low[u], dfn[v]);
    }
}

// 暴力方法找到lca(a,b)
int lca(int a, int b) {
    if (dfn[a] > dfn[b]) swap(a, b); // 利有dfn的物理含义,可以知道a和b的最终生成树中的深度关系

    // 本题与什么缩点没有一毛钱关系,只是在讨论割边。
    // 整体思路就是用Tarjan算法求出割边,然后尝试连接(a,b),如样例的图所示
    // b节点,满足b在a下面就一直向上,直到不能再上,再上就跑到a上面去了为止
    // 对于两个深度的节点a,b,先将a,b对齐到同一高度
    while (dfn[a] < dfn[b]) {
        if (bridge[b]) bcnt--;
        bridge[b] = 0;
        b = p[b];
    }
    // 然后再两个节点一起向上跳,这是为了照顾效率吗?
    while (a != b) {
        if (bridge[a]) bridge[a] = 0, bcnt--; // 如果p[a]->a是割边的话,那么现在因为有了环,将不再是割边
        if (bridge[b]) bridge[b] = 0, bcnt--; // 如果p[b]->b是割边的话,那么现在因为有了环,将不再是割边
        a = p[a], b = p[b];                   // 一路向上
    }

    return a;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("POJ3694.in", "r", stdin);
#endif
    int cas = 0;
    while (~scanf("%d%d", &n, &m)) {
        if (n == m && n == 0) break;

        ts = bcnt = 0;
        memset(h, -1, sizeof h);               // 初始化链式前向星
        memset(low, 0, sizeof low);            // 初始化Tarjan算法需要的数组
        memset(dfn, 0, sizeof dfn);            // 初始化Tarjan算法需要的数组
        memset(bridge, 0, sizeof bridge);      // 记录某条边是不是桥
        for (int i = 1; i <= n; i++) p[i] = i; // 初始化并查集

        printf("Case %d:\n", ++cas);
        while (m--) {
            int a, b;
            scanf("%d%d", &a, &b);
            add(a, b), add(b, a);
        }

        // 因为是无向则连通的图,所以所有点必然都连通,也就是从1号点可以到达任意点,
        // 不需要枚举每个点,并且判断dfn[i]==0进行判断
        tarjan(1, 0);

        // q次询问
        int q;
        scanf("%d", &q);
        while (q--) {
            // 添加边,如果边的两端处在不同的点上,求他们的LCA,并且减少桥的数量
            int a, b;
            scanf("%d%d", &a, &b);
            lca(a, b);
            printf("%d\n", bcnt); // 割边数量
        }
        puts("");
    }
    return 0;
}

标签:缩点,bridge,Network,int,3694,割边,dfn,POJ,low
From: https://www.cnblogs.com/littlehb/p/17578803.html

相关文章

  • (Relax 数论1.26)POJ 1496 Word Index(计算一个字符串在字典中的位置)
    大致题意:(与POJ1850基本一致)输出某个str字符串在字典中的位置,由于字典是从a=1开始的,因此str的位置值就是在str前面所有字符串的个数+1规定输入的字符串必须是升序排列。不降序列是非法字符串要求用循环输入,输入若干组字符串,若输入非法字符串则输出0,但不结束程序,这是和POJ1850......
  • POJ 1458 Common Subsequence(动态规划)
    传送门代码如下:#include<iostream>#include<cstdio>usingnamespacestd;intmaxLen[1000][1000];intmain(){strings1,s2;while(cin>>s1>>s2){intlength1=s1.length();intlength2=s2.length();......
  • Creating network "docker_default" with the default driver ERROR: Failed to S
    创建网络"docker_default"withthedefaultdriverERROR:FailedtoS在使用Docker容器时,有时会遇到以下错误信息:Creatingnetwork"docker_default"withthedefaultdriverERROR:FailedtoS。这个错误通常表示Docker无法创建名为"docker_default"的网络。本文将解释此错......
  • 题解 POJ3318【Matrix Multiplication】
    postedon2022-10-2119:56:08|under题解|sourceproblem判断三个\(n\timesn\)的矩阵是否满足\(A\timesB=C\),\(n\leq500\)。solution随机一个行向量\(v\)。若\(a\timesb=c\),则有\(v\timesa\timesb=v\timesc\)(不充分)。显然相乘复杂度仅为\(O(n^2)\)。类似于......
  • Android GO 版本源码中preferred network type显示
    AndroidGO版本源码中preferrednetworktype的显示作为一名经验丰富的开发者,我将向你解释如何在AndroidGO版本的源码中实现"preferrednetworktype"的显示。下面是实现这个功能的步骤:步骤概览步骤动作步骤1创建一个新的Android项目步骤2添加必要的权限步骤......
  • poj 1716 Integer Intervals (贪心)
    题意:给定n个连续的区间,求一个集合。其中,n个区间的每一个区间都至少包含两个这个集合的元素。求这个集合元素的最小值。 题解:1、先将所有区间按终点从小到大排序。2、我们先取第一个区间(排好序的区间)的两个值,因为要使得结果最优,我们肯定选择最接近终点的那两个值。假设一个为Selem,......
  • poj 1844 sum (数学)
    题意:给出一个数S,从1到N个数,每个数前面可以是负号或者是正号,这样累加起来,结果可以等于S,问最小的N是多少。题解:因为从1一直加到n的值(假设为sum(n))等于sum的n是最小的。所以我们先算出sum(n)大于等于sum的那个n。这样我们可以得出一个值m=sum(n)-sum.如果m==0那么n就是我们要求的......
  • poj 2311 Cutting Game (sg函数)
    小记:这题是对sg函数的初步理解。对于sg函数只要g[x]==0,则此时为必败态。x作为后继,我们就要对所有的后继进行标记,然后mex之。因为每次只能切一刀,所以切完之后,会有两块方格,而对每一块方格进行游戏又会有一个sg函数值,所以根据sg函数的性质,它这一刀所代表的后继,即为这两块方格的sg函......
  • poj 1632 Vase collection
    题意:有n个花瓶,每个花瓶都带有两种属性-形状和颜色,而每种属性都有36种不同的状态。求最大的k,使得k*k个花瓶的形状和颜色都有k种状态,且k*k个花瓶的两种属性都是由形状和颜色的k种状态组合而成的。题解:我们用一个数组(comb[])存放形状和颜色,数组的下标为形状,然后将颜色状态压缩成为数组......
  • poj 1777 Vivian's Problem
    题意:给出K个数,p1,p2,……pk,不一定是素数,给这些数添加指数,0-10之间,最终的乘积为n,他的所有因子和为m,问是否存在一个m为2的幂,如果有多个输出最大的指数,如果没有输出NO。梅森素数 我们把满足E=2^i-1的素数E称作梅森素数。关于梅森素数,有一个重要的定理:“一个数能够写成几个......