P11251 [GESP202409 八级] 美丽路径

GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。

一、先回答问题

结论

只保留异色相邻边,问题变成求森林中最长路径的节点数。

容易误入的路

直接求原树直径会经过同色相邻边,不一定是美丽路径。

二、核心观察

建模与压缩

对每个点维护向下最长和次长的美丽链长度。

为什么不丢答案:经过一个点的最长路径只能由两条不同子方向的最长美丽链拼起来。

颜色 d1/d2 答案

三、互动演示

步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推

四、自己选答案

问题 1

长度按什么计算?

问题 2

同色边怎么处理?

五、手推结果

状态意义
d1向下最长美丽链
d2向下次长美丽链
d1+d2+1经过 u 的答案

课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。

六、C++14 参考代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
vector<int> adj[MAXN];
int color[MAXN];
bool visited[MAXN];
int ans = 0;

// d1 表示从 u 向下延伸的最长美丽路径(边数)
// d2 表示从 u 向下延伸的次长美丽路径(边数)
int dfs(int u, int p) {
    visited[u] = true;
    int d1 = 0, d2 = 0;
    
    for (int v : adj[u]) {
        if (v == p) continue;
        // 只有颜色不同才能继续延伸美丽路径
        if (color[u] != color[v]) {
            int d = dfs(v, u);
            if (d > d1) {
                d2 = d1;
                d1 = d;
            } else if (d > d2) {
                d2 = d;
            }
        } else {
            // 颜色相同,虽然不延伸当前路径,但也要访问 v 确保处理所有连通块
            if (!visited[v]) {
                dfs(v, u);
            }
        }
    }
    
    // 更新全局最长路径(节点数 = 边数 + 1)
    ans = max(ans, d1 + d2 + 1);
    // 返回以 u 为顶点的最长向下路径长度(节点数)
    return d1 + 1;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    if (n == 0) {
        cout << 0 << endl;
        return 0;
    }
    
    for (int i = 1; i <= n; i++) {
        cin >> color[i];
    }
    
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    
    ans = 0;
    for (int i = 1; i <= n; i++) {
        if (!visited[i]) {
            dfs(i, 0);
        }
    }
    
    cout << ans << endl;
    
    return 0;
}