P11379 [GESP202412 八级] 树上移动

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

一、先回答问题

结论

n 只有 1000,枚举起点 DFS,维护路径黑点数和长度即可。

容易误入的路

看到树上最长路径就套直径,但黑点限制会破坏普通直径性质。

二、核心观察

建模与压缩

固定起点后,到每个终点的路径唯一,DFS 中路径状态足够判断合法。

为什么不丢答案:枚举所有起点,等价于枚举所有无重复简单路径的两个端点。

起点 s 黑点数 路径长度

三、互动演示

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

四、自己选答案

问题 1

起点颜色算不算?

问题 2

为什么 O(n^2) 可过?

五、手推结果

状态含义
u当前点
b已过黑点数
tot已过节点数

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

六、C++14 参考代码

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

const int MAXN = 1005;
vector<int> adj[MAXN];
int color[MAXN];
int n, k;
int ans = 0;

// u: 当前节点, p: 父节点, b: 当前路径黑点数, tot: 当前路径总点数
void dfs(int u, int p, int b, int tot) {
    if (b > k) return; // 超过黑点限制,停止
    
    ans = max(ans, tot); // 更新最大节点数
    
    for (int v : adj[u]) {
        if (v == p) continue;
        dfs(v, u, b + color[v], tot + 1);
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin >> n >> k;
    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);
    }
    
    // 枚举每一个点作为起点
    for (int i = 1; i <= n; i++) {
        dfs(i, -1, color[i], 1);
    }
    
    cout << ans << endl;
    
    return 0;
}