HDU - 3394 Railway(连通分量+环)

题目大意:有一个人奇怪的人想要铺路,这个人想把每个环都铺上石头,但是铺石头时不能重复铺,如果重复铺的话,这条边就算损坏

问这个人要损坏多少条边,有多少条边可以不用铺石头

解题思路:不用铺石头的边肯定是桥,因为桥不属于任意一个环

接着判断一下有多少条边会冲突,首先,一个环的话肯定是点-双连通,但点-双连通不一定是个环,所以这个要判断一下。

一个正常的环是要满足 边数=点数 的,如果边数比点数多了,证明这个环被分割成至少三个环了(一个最外围的环,两个被分的小环),可以看出,这三个环每条边都冲突

所以只要满足边数>点数的,都可以证明这个环的所有边冲突

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define N 10010
#define M 200010
#define min(a,b) ((a) < (b) ? (a): (b))

struct Edge{
    int from, to, next, id;
    Edge() {}
    Edge(int from, int to) :from(from), to(to){}
}E[M];

int n, m, tot, dfs_clock, bnum, bcc_cnt, top;
int head[N], pre[N], stack[M], low[N], num[N];
vector<int> edge[N];
bool vis[N];

void AddEdge(int u, int v) {
    E[tot].from = u; E[tot].to = v; E[tot].next = head[u]; E[tot].id = tot; head[u] = tot++;
    u = u ^ v; v = u ^ v; u = v ^ u;
    E[tot].from = u; E[tot].to = v; E[tot].next = head[u]; E[tot].id = tot; head[u] = tot++;
}

void init() {
    memset(head, -1, sizeof(head));
    tot = 0;

    int u, v;
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &u, &v);
        AddEdge(u, v);
    }
}

void dfs(int u, int fa) {
    pre[u] = low[u] = ++dfs_clock;
    for (int i = head[u]; i != -1; i = E[i].next) {
        int v = E[i].to;
        if (!pre[v]) {
            stack[++top] = E[i].id;
            dfs(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] >= pre[u]) {
                bcc_cnt++;
                edge[bcc_cnt].clear();
                int x, id;
                while (1) {
                    id = stack[top--];
                    edge[bcc_cnt].push_back(id);
                    x = E[id].from;
                    if (x == u)
                        break;
                }
                if (low[v] > pre[u])
                    bnum++;
            }
        }
        else if(pre[v] < pre[u] && v != fa) {
            low[u] = min(low[u], pre[v]);
            stack[++top] = E[i].id;
        }
    }
}

void solve() {
    memset(pre, 0, sizeof(pre));
    dfs_clock = bcc_cnt = bnum = top = 0;
    for (int i = 0; i < n; i++)
        if (!pre[i])
            dfs(i, -1);
    int ans = 0;
    for (int i = 1; i <= bcc_cnt; i++) {
        for (int j = 0; j < n; j++)
            vis[j] = false;
        int size = edge[i].size(), cnt = 0;
        for (int j = 0; j < size; j++) {
            if (!vis[E[edge[i][j]].from]) {
                vis[E[edge[i][j]].from] = true;
                cnt++;
            }
            if (!vis[E[edge[i][j]].to]) {
                vis[E[edge[i][j]].to] = true;
                cnt++;
            }
        }
        if (size > cnt)
            ans += size;
    }
    printf("%d %d\n", bnum, ans);
}

int main() {
    while (scanf("%d%d", &n, &m) != EOF && n + m) {
        init();
        solve();
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-25 10:58:45

HDU - 3394 Railway(连通分量+环)的相关文章

HDU 3394 Railway(点双连通分量的应用)

题意:给定一个无向图,分别求出不在任何环中的边的数量和同时在两个或以上的环中的边的数量. 解法:桥上的边就是不在任何环中的.而如果一个点双连通分量中边的数量比点的数量要多,那么该双连通分量的所有边都同时在两个或以上的环中(这个可以想象一下,在一个简单环中多加一条端点不同的边,这样简单环就会被分割成两个小的简单环,任何一条在大的环中的边都会同时处于一个其中一个小的环中). 在tarjan算法中,当lowv>pre[u]时(u是起始点,v是边的下一个点),边(u,v)就是一条桥.而点双连通分量的边的

HDU 3394 Railway (点双联通+圈内判边数)

大意:给定M条边,问有有多少边是不在环上(或环内)的,有多少边是有冲突的(什么是冲突?即在一个环内有多条边将环分割开,即这样的边+上环上边的总数) 思路:求桥的个数比较容易处理,直接(low[v]>dfn[u]即可)主要是怎么找冲突边,我们知道他们一定在一个联通分量内,所以我们将求出的 一组联通分量拿出来,进行遍历看是否所有的连的边在当前的栈中,在的话边数++,因为是无向图,所以最后要除二与联通分量的点相比较即可. 前向星: #include<map> #include<queue

Railway HDU - 3394 (点双连通)

Railway HDU - 3394 题意:一个无向图,1求不在任何一个环里的边数:2求在不止一个环里的边数. 第一问明显就是求桥,第二问,如果求出的某个点双连通分量里面边数多于点数,说明不止一个环,那么所有的边都在不止一个环里. 该求点双连通的,,求成了边双连通...要仔细分析问题. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 us

[转载]HDU 3478 判断奇环

题意:给定n个点,m条边的无向图(没有重边和子环).从给定点出发,每个时间走到相邻的点,可以走重复的边,相邻时间不能停留在同一点,判断是否存在某个时间停留在任意的n个点. 分析: (1)首先,和出发点的位置没有关系.因为可以走重复的边,且时间没有限制大小. (2)图必须是联通的 (3) 1)图为:2-0-1-3 从0点出发(时间为0),一个时间后到达1或2(时间为1),再一个时间后到达0或3(时间为2)... 可以发现,点分为两类,奇数时间到达和偶数时间到达,答案为NO 2)图为:2-0-1-2

HDU 3089 (快速约瑟夫环)

Josephus again Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 652    Accepted Submission(s): 181 Problem Description In our Jesephus game, we start with n people numbered 1 to n around a circle

!HDU 4345 置换群的环长有多少种-dp

题意:置换群,问环的长度有多少种 分析: 环的长度就是每次你选择的区间长度的最小公倍数.总区间长度是n,那么这道题就是求和等于n的各个数的最小公倍数有多少种. 状态:dp[i][j]前i个质数,区间长度不超过j的最优解 转移:dp[i][j]=dp[i-1][j]+dp[i-1][j-k],k=prim[i]^1,2... 这题和上一题的dp难哭我了. 代码: #include<iostream> #include<cstdio> #include<algorithm>

hdu 1213 求连通分量(并查集模板题)

求连通分量 Sample Input2 //T5 3 //n m1 2// u v2 34 5 5 12 5 Sample Output24 1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <cmath> 6 # include <queue> 7 # define LL long

HDU 3980 (SG 环变成链 之前的先手变成后手)

题意 两个人在一个由 n 个玻璃珠组成的一个圆环上玩涂色游戏,游戏的规则是: 1.每人一轮,每轮选择一个长度为 m 的连续的.没有涂过色的玻璃珠串涂色 2.不能涂色的那个人输掉游戏 Aekdycoin 先手 开始时候是一个环,第一个人涂色后就变成了链,这时候就可以使用尼姆博弈了.但是注意一点此时第二个人应该变成尼姆博弈中的先手了. 第一次涂色后 变成 abcdxyzk 先手 n < m 先手无法涂 后手胜 Sample Input2 3 1 // n m4 2 Sample OutputCase

HDU 2242 双连通分量 考研路茫茫——空调教室

思路就是求边双连通分量,然后缩点,再用树形DP搞一下. 代码和求强连通很类似,有点神奇,=_=,慢慢消化吧 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <vector> 5 #include <stack> 6 using namespace std; 7 8 const int maxn = 10000 + 10; 9 const int