uva5135

题意是给你一个无向图,让你求至少要放多少个太平井,使得图中任意一个点坏掉,在其它所有点的人都能通过安全井逃生。

显然先求出BCC(点双)。

显然把井放在割顶不合适,因为如果割顶挂了,这个放在割顶的井不能帮助任何人逃生。

然后对于一个BCC,如果它只有一个割顶,那么一定要在非割顶处放一口井,因为如果割顶挂了,剩下的人就和大部队分离开来,他们需要一口井,这口井放在BCC中除了割顶的任意一个点都可以。

如果一个BCC有两个割顶,那么就无关紧要了。因为题目中说,最多只会有一个点被破坏,所以如果有两个割顶的话,一个坏了,剩下的人还可以通过另一个跑掉。

所以做法就是跑一遍tarjan求出所有的BCC,然后对于只有一个割顶的BCC,要在BCC中除了割顶的任意一点放置一口井,对于两个或以上的,不用放井。

BCC中若无割顶,则说明整个图是个BCC。

否则BCC中一定有一个割顶。(这句话可以换一个表述,两个BCC之间一定是由割顶相连,如果没有割顶相连,则这两个BCC可以合并为一个割顶)

最后乘法原理一波带走。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <vector>
#define LL long long

using namespace std;

const LL maxn = 5 * 1e4 + 5, maxm = maxn * 2;

LL N, n, tot, dfs_clock, bcc_cnt, ans1, ans2;

LL h[maxn], low[maxn], dfn[maxn], bccno[maxn], iscut[maxn];

struct edge
{
    LL v, next;
}a[maxm];

struct EDGE
{
    LL u, v;
};

vector<LL> bcc[maxn];

stack<EDGE> s;

void add(LL x, LL y)
{
    a[tot].v = y;
    a[tot].next = h[x];
    h[x] = tot++;
}

LL dfs(LL u, LL fa)
{
    LL lowu = dfn[u] = ++dfs_clock;
    LL child = 0;
    for (LL i = h[u]; ~i; i = a[i].next)
    {
        LL v = a[i].v;
        EDGE e = (EDGE){u, v}; 
        if (!dfn[v])
        {
            s.push(e);
            child++;
            LL lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if (lowv >= dfn[u])
            {
                iscut[u] = 1;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                for(;;)
                {
                    EDGE x = s.top(); s.pop();
                    if (bccno[x.u] != bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(x.u);
                        bccno[x.u] = bcc_cnt;
                    }if (bccno[x.v] != bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(x.v);
                        bccno[x.v] = bcc_cnt;
                    }
                    if (x.u == u && x.v == v) break;
                }
            }
        }
        else if (dfn[v] < dfn[u] && v != fa)
            {
                s.push(e);
                lowu = min(lowu ,dfn[v]);
            }
    }
    if (fa == 0 && child == 1)
    {
        iscut[u] = 0;
    }
    return low[u] = lowu;
} 

int main()
{
//    freopen("uva5135.in","r",stdin);
    LL kase = 0;
    while (scanf("%lld", &N) && N)
    {
        memset(h, -1, sizeof h); tot = bcc_cnt = dfs_clock = 0;
        memset(iscut, 0, sizeof iscut);
        memset(dfn, 0, sizeof dfn);
        memset(low, 0, sizeof low);
        memset(bccno, 0, sizeof bccno);
        LL n = 0;
        for (LL i = 1; i <= N; i++)
        {
            LL x, y;
            scanf("%lld%lld", &x, &y);
            add(x, y); add(y, x);
            n = max(n, max(x, y));
        }
        dfs(1, 0);
        
        ans1 = 0; ans2 = 1;
        if (bcc_cnt == 1)
        {
            ans1 = 2; ans2 = n * (n - 1) / 2;
        }else
        for (LL i = 1; i <= bcc_cnt; i++)
        {
            LL cut_cnt = 0;
            for (LL j = 0; j < bcc[i].size(); j++)
                if (iscut[bcc[i][j]])
                    cut_cnt++;
            if (cut_cnt == 1)
            {
                ans1++; ans2 *= bcc[i].size() - 1;
            }
        }
        printf("Case %lld: %lld %lld\n", ++kase, ans1, ans2);
    }
    return 0;
}

还有一个做法是先找出割顶,然后从非割顶的点开始dfs,dfs的过程中不许经过割顶。稍后补坑。

posted @ 2017-11-02 12:05  yohanlong  阅读(152)  评论(0编辑  收藏  举报