P8028 [COCI 2021/2022 #3] Cijanobakterije 题解

有个彩蛋

题目描述

一位微生物学家有 \(n\) 个蓝藻细菌。这些细菌中有 \(m\) 组细菌 \((a_i,b_i)\),表示 \(a_i\)\(b_i\) 之间有一条生物链。若干条生物链顺次连接之后可组成长链。长链的长度定义为这条长链上的细菌数量。

现可在细菌之间两两添加若干条生物链,使得添加之后的所有生物链均不存在环。

求在进行若干次添加生物链的操作后,最长的长链的长度是多少。

输入格式

第一行,两个整数 \(n,m\)

接下来的 \(m\) 行,每行两个正整数 \(a_i,b_i\),表示 \(a_i,b_i\) 两个细菌之间有一条生物链。数据保证 \(a_i \neq b_i\) 且同一条生物链只会出现一次,同时这 \(m\) 条生物链不会存在环。

输出格式

输出最长的长链的长度。

输入输出样例 #1

输入 #1

100 0

输出 #1

100

输入输出样例 #2

输入 #2

8 6
1 2
1 3
1 4
5 6
5 7
5 8

输出 #2

6

输入输出样例 #3

输入 #3

6 5
1 2
2 3
3 4
4 6
4 5

输出 #3

5

说明/提示

【样例 2 解释】

\(2\)\(6\) 之间添加一条生物链后,最长的长链为 \(3-1-2-6-5-7\),长度为 \(6\)

【数据规模与约定】

本题采用子任务捆绑测试。

  • Subtask 1(15 pts):\(m=n-1\)
  • Subtask 2(6 pts):\(b_i=a_i+1\)
  • Subtask 3(6 pts):\(1 \le a_i \le 2\)
  • Subtask 4(15 pts):\(1 \le n \le 1000\)
  • Subtask 5(28 pts):无特殊限制。

对于 \(100\%\) 的数据,\(1 \le n \le 10^5\)\(0 \le m \lt n\)\(1 \le a_i,b_i \le n\)

【提示与说明】

题目译自 COCI 2021-2022 CONTEST #3 Task 2 Cijanobakterije

本题分值按 COCI 原题设置,满分 \(70\)


题面很简单,就是会构建很多连通块,每个连通块都是
题目提到最大,很容易想到树的直径,让每一个树的直径 首尾顺次相接,自然可以得到最大值
如果不知道什么是树的直径可以点链接看看题解

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e5 + 2;

vector<int> a[N];
int d[N], fl[N];

vector<int> f;

inline void dfs(int now) {
    f.push_back(now);
    for (auto i : a[now]) {
        if (!d[i]) {
            d[i] = d[now] + 1;
            dfs(i);
        }
    }
}

signed main() {
    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;

        a[u].push_back(v);
        a[v].push_back(u);
    }


    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (fl[i]) continue;
        f.clear();
        memset(d, 0, sizeof d);
        d[i] = 1;
        dfs(i);
        int pos = 0;
        int mx = 0;
        for (auto j : f) {
            if (d[j] > mx) {
                mx = d[j];
                pos = j;
            }
        }
        f.clear();
        memset(d, 0, sizeof d);
        d[pos] = 1;
        dfs(pos);
        mx = 0;
        for (auto j : f) {
            mx = max(d[j], mx);
            fl[j] = true;
        }
        ans += mx;
    }

    cout << ans;
    return 0;
}

然后会发现\(42pts\),原来是\(memset\), 于是你加了时间戳,我甚至写了快读

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 1e5 + 2;

vector<int> a[N];
int d[N][2], fl[N], t = 0;
// 0是时间点,1是值
vector<int> f;

inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + (ch - '0');
        ch = getchar();
    }
    return x * f;
}

inline void dfs(int now) {
    f.push_back(now);
    d[now][0] = t;
    for (auto i : a[now]) {
        if (d[i][0] != t) {
            d[i][1] = d[now][1] + 1;
            dfs(i);
        }
    }
}

signed main() {
    int n = read(), m = read();

    for (int i = 1; i <= m; i++) {
        int u = read(), v = read();

        a[u].push_back(v);
        a[v].push_back(u);
    }


    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (fl[i]) continue;
        f.clear();
        d[i][1] = 1;
        t++;
        dfs(i);
        int pos = 0;
        int mx = 0;
        for (auto j : f) {
            if (d[j][1] > mx) {
                mx = d[j][1];
                pos = j;
            }
        }
        
        f.clear();
        d[pos][1] = 1;
        t++;
        dfs(pos);
        mx = 0;
        for (auto j : f) {
            mx = max(d[j][1], mx);
            fl[j] = true;
        }
        ans += mx;
    }

    cout << ans;
    return 0;
}

\(nice!\)AC记录

彩蛋

不是有一个\(42pts\)代码吗?写上快读和一点优化be like:
image
比代码1快一点,代码1.19ms

posted @ 2026-03-26 22:52  PCMSFV  阅读(3)  评论(1)    收藏  举报