zoukankan      html  css  js  c++  java
  • 【tarjan/v-DCC】Redundant Paths POJ

    题目

    题目链接:http://poj.org/problem?id=3177

    In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the rest of the herd are forced to cross near the Tree of Rotten Apples. The cows are now tired of often being forced to take a particular path and want to build some new paths so that they will always have a choice of at least two separate routes between any pair of fields. They currently have at least one route between each pair of fields and want to have at least two. Of course, they can only travel on Official Paths when they move from one field to another.

    Given a description of the current set of R (F-1 <= R <= 10,000) paths that each connect exactly two different fields, determine the minimum number of new paths (each of which connects exactly two fields) that must be built so that there are at least two separate routes between any pair of fields. Routes are considered separate if they use none of the same paths, even if they visit the same intermediate field along the way.

    There might already be more than one paths between the same pair of fields, and you may also build a new path that connects the same fields as some other path.

    输入描述

    Line 1: Two space-separated integers: F and R

    Lines 2..R+1: Each line contains two space-separated integers which are the fields at the endpoints of some path.

    输出描述

    Line 1: A single integer that is the number of new paths that must be built.

    样例

    样例输入

    7 7

    1 2

    2 3

    3 4

    2 5

    4 5

    5 6

    5 7

    样例输出

    2

    题意

    给你一张无向图,问你最少加几条边使它变成边双连通图。

    题解

    题目有重边,有无重边不影响答案但影响我们计算,所以先去重边,这里为了省事直接用map去重。

    使用tarjan找到图中所有的边双连通分量,缩点后,问题就变成问一颗树最少加几条边使它变成边双连通图。很容易知道,我们只要把度数为1的那种末端节点相连,就可以花最少的边将树变成双连通图。

    题目很简单,这里把它拿出来单独写一篇题解是因为想讲讲一种特殊的实现方法。

    一般e-DCC缩点的求法是先标记割边然后,dfs遍历图,染色得出e-DCC,和v-DCC缩点的dfs中使用栈维护v-DCC不太一样,但我在写v-DCC缩点的时候,发现这种栈维护DCC的操作完全也可以用在e-DCC里,只不过要稍微修改,首先,要v-DCC要加入割点,e-DCC不用,所以一般v-DCC栈弹出后加入当前节点x的操作就不需要了,直接删除,然后再把v-DCC的dfn[x]<=low[y]

    改成dfn[x]<low[y]就完事,但因为e-DCC是对边操作,会少算根节点,这时我使用了一个比较取巧的方法,直接建一条新边(0,1),使(0,1)必为割边,这样就可以正确的计算根节点了,计算完再把这条边去除,代码实现上也很简单。

    这样就可以实现v-DCC缩点和e-DCC缩点代码上的统一。

    这边代码中缩点采取的非常简单粗暴的方法,实际上有很好的写法,因为最后的结果一定是一颗树,并且边都是割边。

    AC代码

    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<ctime>
    #include<string>
    #include<vector>
    #include<map>
    #include<list>
    #include<set>
    #include<stack>
    #include<bitset>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<ll, ll> pii;
    typedef pair<ll, ll> pll;
    const ll N = 3e5 + 5;
    const ll mod = 998244353;
    const ll INF = 0x3f3f3f3f;
    const ll INF64 = 0x3f3f3f3f3f3f3f3f;
    const double gold = (1 + sqrt(5)) / 2.0;
    const double PI = acos(-1);
    const double eps = 1e-8;
    ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
    ll pow(ll x, ll y, ll mod) { ll ans = 1; while (y) { if (y & 1)ans = (ans * x) % mod; x = (x * x) % mod; y >>= 1; }return ans; }
    ll pow(ll x, ll y) { ll ans = 1; while (y) { if (y & 1)ans = (ans * x) % mod; x = (x * x) % mod; y >>= 1; }return ans; }
    ll inv(ll x) { return pow(x, mod - 2); }
    
    vector<int>e[N];
    set<int>e2[N];
    vector<int>dcc[N];
    stack<int>s;
    int vis[N];
    int cnt;
    int dfn[N], low[N];
    int cut[N];
    int tol = 0;
    
    int n, m;
    void tarjan(int x, int f) {
        dfn[x] = ++tol;
        low[x] = dfn[x];
        s.push(x);
        for (int i = 0; i < e[x].size(); i++) {
            int y = e[x][i];
            if (!dfn[y]) {
                tarjan(y, x);
                low[x] = min(low[x], low[y]);
    
                if (dfn[x] < low[y]) {
                    cnt++;
                    int z;
                    do {
                        z = s.top();
                        s.pop();
                        vis[z] = cnt;
                        dcc[cnt].push_back(z);
                    } while (z != y);
    
    
    
                }
            }
            else if (y != f) {
                low[x] = min(low[x], dfn[y]);
            }
    
        }
    
    }
    
    map < pll, bool>mp;
    
    int main() {
        int n, m;
        int x, y;
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; i++) {
            scanf("%d%d", &x, &y);
            if (x > y)swap(x, y);
            bool &tmp = mp[pll(x, y)];
            if (tmp)continue;
            tmp = 1;
            e[x].push_back(y);
            e[y].push_back(x);
        }
    
        e[0].push_back(1);
        e[1].push_back(0);
        tarjan(0, -1);
        e[0].clear();
        e[1].pop_back();
        for (int i = 1; i <= cnt; i++) {
            for (int j = 0; j < dcc[i].size(); j++) {
                int x = dcc[i][j];
                for (int k = 0; k < e[x].size(); k++) {
                    int y = vis[e[x][k]];
                    if (y == i)continue;
                    e2[i].insert(y);
                    e2[y].insert(i);
                }
            }
        }
        int ans=0;
        for (int i = 1; i <= cnt; i++) {
            if (e2[i].size() == 1)ans++;
        }
        printf("%d
    ", (ans + 1) / 2);
    
        
        return 0;
    }
  • 相关阅读:
    linux下光标定位和清屏函数
    使用NHibernate的时候出现“指定的转换无效”的错误
    UI 界面:技术决定一切
    BeamTarget红外线的末尾
    Chicken的代码解剖:6 PlayerController
    kismet模块创建
    Projectile重构
    关卡设计师优化关卡
    一些主类的继承关系
    EA iOS平台《质量效应》《死亡空间》的界面
  • 原文地址:https://www.cnblogs.com/komet/p/15139097.html
Copyright © 2011-2022 走看看