P3225 [HNOI2012] Tarjan v-DCC

题意

传送门 P3225 [HNOI2012]矿场搭建

题解

节点坍塌即将节点及其相连边从图中删去,考虑割点。为了保证任一节点坍塌后图中仍存在救援点,则图中至少存在两个救援点。若 v − D C C v-DCC vDCC 中非割点坍塌,其余节点可以从割点向外走到某个救援节点;若 v − D C C v-DCC vDCC 割点坍塌,若 v − D C C v-DCC vDCC 中不存在其余割点,则需要在任一非割点处建立救援点,否则,可以从其余割点向外走到某个救援节点。

推论:若图中存在割点,则图中仅有 1 1 1 个割点的 v − D C C v-DCC vDCC 至少存在 2 2 2 个。

证明。若图中仅存在割点数为 1 1 1 v − D C C v-DCC vDCC,割点至少分割出两个 v − D C C v-DCC vDCC。若图中存在割点数大于 1 1 1 v − D C C v-DCC vDCC,设其编号为 x x x,这样的 v − D C C v-DCC vDCC 至少向 2 2 2 个割点处的其它 v − D C C v-DCC vDCC 保持连通性,设其编号为 y , z y,z y,z。若 y , z y,z y,z 割点数为 1 1 1,推论显然成立。否则,以 y y y 为例,反证法,将 x x x 从图中删去,此时 y , z y,z y,z 不连通,此时若 y y y 不与某个割点数为 1 1 1 v − D C C v-DCC vDCC 连通,则 y y y 所连接的 v − D C C v-DCC vDCC 都至少对 2 2 2 个方向保持连通性, y y y 将与其他 v − D C C v-DCC vDCC 构成环,这与 v − D C C v-DCC vDCC 的极大性矛盾。

若图中存在割点,则最少点数为图中割点数为 1 1 1 v − D C C v-DCC vDCC 的数量,方案数为这些 v − D C C v-DCC vDCC 中非割点数量之积。若图中无割点,则最少点数为 2 2 2,方案数为 C N 2 \text C_{N}^{2} CN2

割点数为 1 1 1 v − D C C v-DCC vDCC 至少包含 1 1 1 个非割点,则 T a r j a n Tarjan Tarjan 求解过程中可以不使用栈维护 v − D C C v-DCC vDCC,仅求出割点;然后仅对存在非割点的 v − D C C v-DCC vDCC 进行 D F S DFS DFS

题目疑似默认点编号为从 1 1 1 开始至输入节点的最大值 ( N > 1 N>1 N>1)的连续编号,且图为连通图。否则,代码中需要对编号进行离散化,并依次处理各联通分量。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 505, maxm = maxn * 2;
int T, N, M, num, dfn[maxn], low[maxn];
int tot, head[maxn], to[maxm], nxt[maxm];
int cnt, sum, res1, vsc[maxn];
bool cut[maxn], vs[maxn];
ull res2;

inline void add(int x, int y) {
    
     to[++tot] = y, nxt[tot] = head[x], head[x] = tot; }

void tarjan(int x, int rt)
{
    
    
    dfn[x] = low[x] = ++num;
    int fg = 0;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (!dfn[y])
        {
    
    
            tarjan(y, rt);
            low[x] = min(low[x], low[y]);
            if (low[y] >= dfn[x])
            {
    
    
                ++fg;
                if (x != rt || fg > 1)
                    cut[x] = 1;
            }
        }
        else
            low[x] = min(low[x], dfn[y]);
    }
}

void dfs(int x, int dcc)
{
    
    
    vs[x] = 1, ++sum;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (!cut[y] && !vs[y])
            dfs(y, dcc);
        else if (cut[y] && vsc[y] != dcc)
            ++cnt, vsc[y] = dcc;
    }
}

int main()
{
    
    
    while (~scanf("%d", &M) && M)
    {
    
    
        memset(dfn, 0, sizeof(dfn));
        memset(cut, 0, sizeof(cut));
        memset(head, 0, sizeof(head));
        N = num = tot = 0, res1 = 0, res2 = 1;
        for (int i = 1, x, y; i <= M; ++i)
            scanf("%d%d", &x, &y), add(x, y), add(y, x), N = max(N, max(x, y));
        tarjan(1, 1);
        memset(vs, 0, sizeof(vs));
        memset(vsc, 0, sizeof(vsc));
        bool _cut = 0;
        for (int i = 1, k = 0; i <= N; ++i)
        {
    
    
            _cut |= cut[i];
            if (!cut[i] && !vs[i])
            {
    
    
                cnt = sum = 0, dfs(i, ++k);
                if (cnt == 1)
                    ++res1, res2 *= sum;
            }
        }
        if (!_cut)
            res1 = 2, res2 = N * (N - 1) / 2;
        printf("Case %d: %d %llu\n", ++T, res1, res2);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114885669
今日推荐