P3225 [HNOI2012]矿场搭建 tarjan割点

这个题需要发现一点规律,就是先按割点求块,然后求每个联通块中有几个割点,假如没有割点,则需要建两个出口,如果一个割点,则需要建一个出口,2个以上不用建。

题干:

题目描述

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。

请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入输出格式
输入格式:

输入文件有若干组数据,每组数据的第一行是一个正整数 N(N<=500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。

输出格式:

输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。

代码:

#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;
#define int long long
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 = 505;
int dfn[N],vis[N],low[N];
bool cut[N];
int num,Cut,tim,root,rs,m,n,ans1,ans2,Case,group;
struct node
{
    int l,r,nxt;
}a[N * N];
int lst[N],len = 0;
void add(int x,int y)
{
    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] = ++tim;
    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])
            {
                if(x != root)
                cut[x] = true;
                else
                rs++;
            }
        }
        else
        {
            if(y != fa)
            low[x] = min(low[x],dfn[y]);
        }
    }
}
void dfs(int x)
{
    vis[x] = group;
    num++;
    for(int k = lst[x];k;k = a[k].nxt)
    {
        int y = a[k].r;
        if(cut[y] && vis[y] != group)
        {
            Cut++;
            vis[y] = group;
        }
        if(!vis[y])
        dfs(y);
    }
}
void init()
{
    clean(vis);
    clean(dfn);
    clean(lst);
    clean(low);
    clean(cut);
    len = 0;ans1 = group = 0;num = 0;
    ans2 = 1;n = 0;tim = 0;Cut = 0;
}
main()
{
    int u,v;
    Case = 1;
    while(cin>>m && m)
    {
        init();
        duke(i,1,m)
        {
            read(u);read(v);
            add(u,v);
            add(v,u);
            n = max(n,max(u,v));
        }
        for(int i = 1;i <= n;i++)
        {
            if(!dfn[i])
            {
                root = i;
                rs = 0;
                tarjan(i,i);
                if(rs >= 2)
                cut[i] = true;
            }
        }
        duke(i,1,n)
        {
            if(!vis[i] && !cut[i])
            {
                ++group;
                num = Cut = 0;
                dfs(i);
                if(Cut == 0)
                {
                    ans1 += 2;
                    ans2 *= (num - 1) * num / 2;
                }
                if(Cut == 1)
                {
                    ans1 += 1;
                    ans2 *= num;
                }
            }
        }
        printf("Case %lld: %lld %lld\n",Case++,ans1,ans2);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/DukeLv/p/10422958.html