CF1000E 大怪兽

1 CF1000E 大怪兽

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

你的朋友在开发一个电脑游戏,他已经想好了游戏世界看起来的样子。游戏世界包括 \(n\) 个通过 \(m\) 个双向路径连接起来的位置。路径设计是全联通的,可以从任何位置出发走到任意一个位置。有些路径上有怪物把守,有些关键路径上有非常可怕的怪物把守,需要英雄设计自己的战术打败他们,这样的怪物称为大怪兽,你需要帮助你的朋友放置这些大怪兽。游戏从位置 \(s\) 开始,到位置 \(t\) 结束,\(s\)\(t\) 具体的位置还没有选定。在选定好这些位置后你的朋友会在从 \(s\)\(t\) 的必经之路上放置大怪兽。你的朋友希望尽可能多的放置大怪兽,这样游戏才有挑战性。他请你计算最大可能的大怪兽个数,注意 \(s\)\(t\) 的位置可以任意选择。

数据范围:\(2≤𝑛≤3\times 10^5, 𝑛−1≤𝑚≤3\times 10^5\)\(n\)\(m\) 分别是位置和路径的个数。

3 题解

很容易发现:如果某个联通块内不存在割边,那么这个联通块内一定没有必经的路径。这是因为将任意一条边删去后,该联通块仍然为联通块,所以这些边就都不是必经的路径了。我们管没有割边的联通块叫做边双联通分量。而我们可以通过找到这个无向图中的所有割边并删除来确定出所有的边双连通分量。在确定出边双连通分量后,我们可以将这些边双连通分量缩成一个点。这样一来,图中的每条边就都是割边了,整个图也就变成一棵树了。

我们容易发现,在这张图上经过必经之路最多的一条路径就是这棵树的直径,因此我们可以在缩点后的新图上用 \(dp\) 求一遍树的直径,直径长度便是答案。

4 代码(空格警告):

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 6e5+10;
#define int long long
int n, m, tot, u, v, cnt, cnt2, ntot, ans;
int head[N], ver[N*2], last[N*2], dfn[N], low[N], c[N];
int nhead[N], nver[N*2], nlast[N*2], dist[N];
bool f[N*2], f2[N];
void add(int x, int y)
{
    ver[++tot] = y;
    last[tot] = head[x];
    head[x] = tot;
}
void nadd(int x, int y)
{
    nver[++ntot] = y;
    nlast[ntot] = nhead[x];
    nhead[x] = ntot;
}
void tarjan(int x, int in_edge)
{
    dfn[x] = low[x] = ++cnt;
    for (int i = head[x]; i; i = last[i])
    {
        int y = ver[i];
        if (!dfn[y])
        {
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if (low[y] > dfn[x]) f[i] = f[i^1] = 1;
        }
        else if (i != ((in_edge)^1)) low[x] = min(low[x], dfn[y]);
    }
}
void dfs(int x)
{
    c[x] = cnt2;
    for (int i = head[x]; i; i = last[i])
    {
        int y = ver[i];
        if (c[y] || f[i]) continue;
        dfs(y);
    }
}
void dp(int x)
{
    f2[x] = 1;
    for (int i = nhead[x]; i; i = nlast[i])
    {
        int y = nver[i];
        if (f2[y]) continue;
        dp(y);
        ans = max(ans, dist[x] + dist[y] + 1);
        dist[x] = max(dist[x], dist[y] + 1);
    }
}
signed main()
{
    tot = 1;
    ntot = 1;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d %d", &u, &v);
        add(u, v);
        add(v, u);
    }
    for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i, 0);
    for (int i = 1; i <= n; i++)
    {
        if (!c[i])
        {
            cnt2++;
            dfs(i);
        }
    }
    for (int i = 2; i <= tot; i++)
    {
        int x = ver[i], y = ver[i^1];
        if (c[x] == c[y]) continue;
        nadd(c[x], c[y]);
        nadd(c[y], c[x]);
    }
    dp(1);
    cout << ans;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-03-17 21:24  David24  阅读(68)  评论(0编辑  收藏  举报