5 条题解

  • 0
    @ 2025-12-15 14:46:46

    数据生成: 仍然卡住

    Case 5 卡住的原因主要在于 I/O 操作和数据查找的效率问题,而非算法死循环。 在 N=105N=10^5 的情况下:

    1. std::set 的瓶颈:原代码使用 std::set 来存储和查找宝物位置。构建 set 需要 O(NlogN)O(N \log N),并且在输出循环中进行了 NN 次查找,总耗时较高。对于 5 组数据,这会产生显著延迟。
    2. vector<vector<int>> 的频繁构造:在 gen_treasures 中,即使是随机撒点(不需要图结构),原代码也会构建一个包含 10510^5 个 vector 的邻接表,这非常消耗内存分配时间。
    3. 用户感知:没有任何进度提示,处理 5 组 10510^5 规模的数据可能需要 3-5 秒,让用户感觉“卡死”。

    下面的代码进行了 极速优化

    1. 移除 std::set:改用 vector<int> 标记宝物,将 O(NlogN)O(N \log N) 的查找降为 O(N)O(N)
    2. 按需构建图:在生成宝物时,只有需要路径算法的策略(如 all_yes)才构建邻接表,Case 5(随机撒点)直接跳过此步骤。
    3. 增加进度条:在控制台输出当前进度(如 Test 1/5),让你知道程序在运行。

    🚀 极速版数据生成器

    /**
     * P11249 [GESP202409 七级] 小杨寻宝 - 极速数据生成器
     * 
     * 优化点:
     * 1. 移除输出时的 std::set 查找,改为 O(N) 的数组标记,极大加速 IO 准备。
     * 2. gen_treasures 中按需构建邻接表,避免 Case 5 进行无意义的 10万次 vector 分配。
     * 3. 增加实时进度显示。
     */
    
    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    #include <fstream>
    #include <random>
    #include <ctime>
    #include <queue>
    #include <numeric> // for std::iota
    
    using namespace std;
    
    // ==========================================
    // Part 1: 标准题解 Solver (迭代 BFS 版,防爆栈)
    // ==========================================
    
    namespace Solver {
        const int MAXN = 100005;
        vector<int> adj[MAXN];
        bool has_treasure[MAXN];
        vector<int> treasure_nodes;
        int distU[MAXN], distV[MAXN];
        int n;
    
        void init(int _n) {
            n = _n;
            for (int i = 1; i <= n; ++i) {
                adj[i].clear();
                has_treasure[i] = false;
            }
            treasure_nodes.clear();
        }
    
        void add_edge(int u, int v) {
            adj[u].push_back(v);
            adj[v].push_back(u);
        }
    
        void set_treasure(int u) {
            if (!has_treasure[u]) {
                has_treasure[u] = true;
                treasure_nodes.push_back(u);
            }
        }
    
        int bfs(int start, int* dist) {
            for (int i = 1; i <= n; ++i) dist[i] = -1;
            queue<int> q;
            q.push(start);
            dist[start] = 0;
            
            int max_dist = -1;
            int farthest_node = start;
            
            while (!q.empty()) {
                int u = q.front();
                q.pop();
                
                if (has_treasure[u]) {
                    if (dist[u] > max_dist) {
                        max_dist = dist[u];
                        farthest_node = u;
                    }
                }
                
                for (int v : adj[u]) {
                    if (dist[v] == -1) {
                        dist[v] = dist[u] + 1;
                        q.push(v);
                    }
                }
            }
            return farthest_node;
        }
    
        string solve() {
            if (treasure_nodes.size() <= 1) return "Yes";
            int U = bfs(treasure_nodes[0], distU);
            int V = bfs(U, distU);
            bfs(V, distV);
            
            int path_len = distU[V];
            for (int t : treasure_nodes) {
                if (distU[t] + distV[t] != path_len) return "No";
            }
            return "Yes";
        }
    }
    
    // ==========================================
    // Part 2: 数据生成器 Generator
    // ==========================================
    
    mt19937 rng(time(0));
    
    int random_int(int l, int r) {
        if (l > r) return l;
        return uniform_int_distribution<int>(l, r)(rng);
    }
    
    // 优化:使用 BFS 找路径
    vector<int> get_path_bfs(int n, int start, int end, const vector<vector<int>>& adj) {
        vector<int> parent(n + 1, 0);
        vector<bool> visited(n + 1, false);
        queue<int> q;
        
        q.push(start);
        visited[start] = true;
        
        bool found = false;
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            if(u == end) {
                found = true;
                break;
            }
            for(int v : adj[u]) {
                if(!visited[v]) {
                    visited[v] = true;
                    parent[v] = u;
                    q.push(v);
                }
            }
        }
        
        vector<int> path;
        if(found) {
            int curr = end;
            while(curr != 0) {
                path.push_back(curr);
                if(curr == start) break;
                curr = parent[curr];
            }
        }
        return path;
    }
    
    vector<pair<int, int>> gen_tree(int n, string type) {
        vector<pair<int, int>> edges;
        edges.reserve(n - 1); // 预分配内存
        if (type == "line") {
            for (int i = 2; i <= n; ++i) edges.push_back({i - 1, i});
        } 
        else if (type == "star") {
            for (int i = 2; i <= n; ++i) edges.push_back({1, i});
        } 
        else { 
            for (int i = 2; i <= n; ++i) {
                int parent = random_int(1, i - 1);
                if (type == "deep" && i > 5) parent = random_int(max(1, i - 5), i - 1); 
                edges.push_back({parent, i});
            }
        }
        return edges;
    }
    
    vector<int> gen_treasures(int n, const vector<pair<int, int>>& edges, string type) {
        vector<int> t_nodes;
        
        // 优化:只有需要图结构的生成策略才构建邻接表
        // Case 5 (random) 不需要这步,能省下大量时间
        vector<vector<int>> local_adj;
        if (type == "all_yes" || type == "force_no") {
            local_adj.resize(n + 1);
            for (auto& e : edges) {
                local_adj[e.first].push_back(e.second);
                local_adj[e.second].push_back(e.first);
            }
        }
    
        if (type == "all_yes") { 
            int u = random_int(1, n);
            int v = random_int(1, n);
            vector<int> path = get_path_bfs(n, u, v, local_adj);
            
            if (!path.empty()) {
                int count = random_int(1, path.size());
                shuffle(path.begin(), path.end(), rng);
                for(int k=0; k<count; ++k) t_nodes.push_back(path[k]);
            } else {
                t_nodes.push_back(1);
            }
        } 
        else if (type == "force_no") {
            int center = -1;
            vector<int> candidates;
            for(int i=1; i<=n; ++i) if(local_adj[i].size() >= 3) candidates.push_back(i);
            
            if (!candidates.empty()) {
                center = candidates[random_int(0, candidates.size()-1)];
                t_nodes.push_back(center);
                int branch_count = 0;
                for(int neighbor : local_adj[center]) {
                    if(branch_count >= 3) break;
                    t_nodes.push_back(neighbor);
                    branch_count++;
                }
            } else {
                vector<int> pool(n);
                iota(pool.begin(), pool.end(), 1);
                shuffle(pool.begin(), pool.end(), rng);
                int count = min(n, 3);
                for(int i=0; i<count; ++i) t_nodes.push_back(pool[i]);
            }
        }
        else { // random
            int count = random_int(1, min(n, 20)); 
            if (random_int(1, 10) > 8) count = random_int(1, n); // 20% 概率生成海量宝物
            
            vector<int> pool(n);
            iota(pool.begin(), pool.end(), 1);
            shuffle(pool.begin(), pool.end(), rng);
            
            for(int i=0; i<count; ++i) t_nodes.push_back(pool[i]);
        }
        
        if (t_nodes.empty()) t_nodes.push_back(1);
        return t_nodes;
    }
    
    void generate_file(int file_id) {
        string in_name = to_string(file_id) + ".in";
        string out_name = to_string(file_id) + ".out";
        ofstream fin(in_name);
        ofstream fout(out_name);
    
        int T = 10;
        if (file_id > 2) T = 5; 
        
        fin << T << endl;
        cout << "Generating Case " << file_id << " (" << T << " tests)... ";
    
        for (int t = 0; t < T; ++t) {
            // 简单的进度反馈
            cout << "." << flush;
    
            int n;
            string tree_type = "random";
            string treasure_type = "random";
    
            if (file_id == 1) { n = random_int(2, 5); }
            else if (file_id == 2) { n = 5; tree_type = (t < 5) ? "line" : "star"; }
            else if (file_id == 3) { n = random_int(500, 1000); }
            else if (file_id == 4) { n = random_int(800, 1000); treasure_type = "all_yes"; }
            else if (file_id == 5) { 
                n = random_int(50000, 100000); 
                tree_type = "line"; 
                treasure_type = "random"; 
            }
            else if (file_id == 6) { 
                n = random_int(50000, 100000); 
                tree_type = "star"; 
                treasure_type = (t % 2 == 0) ? "force_no" : "random"; 
            }
            else if (file_id == 7) { 
                n = random_int(80000, 100000); 
                tree_type = "deep"; 
                treasure_type = "all_yes"; 
            }
            else if (file_id == 8) { 
                n = random_int(80000, 100000); 
                treasure_type = "force_no"; 
            }
            else if (file_id == 9) { 
                n = 100000; 
                if (t < 3) treasure_type = "all_yes";
                else if (t < 6) treasure_type = "force_no";
            }
            else { // Case 10
                n = 100000;
                if (t == 0) { // 极简 Case: 1个宝物
                    fin << n << "\n";
                    for(int i=1; i<=n; ++i) fin << (i==1) << (i==n?"":" ");
                    fin << "\n";
                    vector<pair<int,int>> edges = gen_tree(n, "random");
                    for(auto& e : edges) fin << e.first << " " << e.second << "\n";
                    Solver::init(n);
                    for(auto& e : edges) Solver::add_edge(e.first, e.second);
                    Solver::set_treasure(1);
                    fout << Solver::solve() << "\n";
                    continue;
                }
            }
    
            vector<pair<int, int>> edges = gen_tree(n, tree_type);
            vector<int> treasures = gen_treasures(n, edges, treasure_type);
    
            fin << n << "\n";
            
            // 【关键优化】使用 vector 代替 set 进行 O(1) 查找
            // 解决 N=10^5 时输出极慢的问题
            static vector<int> is_treasure_map; 
            if(is_treasure_map.size() <= n) is_treasure_map.resize(n + 1);
            // 清空标记(只清空用到的部分)
            // 注意:这里为了效率,不使用全局清空,而是先全部置0太慢
            // 更好方法:先全置0? 不,用 fill 也可以,因为 n 很大
            // 为了安全,每次 resize 后 fill 0
            fill(is_treasure_map.begin(), is_treasure_map.begin() + n + 1, 0);
            
            for(int x : treasures) is_treasure_map[x] = 1;
            
            for (int i = 1; i <= n; ++i) {
                fin << is_treasure_map[i] << (i == n ? "" : " ");
            }
            fin << "\n";
            
            for (auto& e : edges) {
                fin << e.first << " " << e.second << "\n";
            }
    
            Solver::init(n);
            for (auto& e : edges) Solver::add_edge(e.first, e.second);
            for (int x : treasures) Solver::set_treasure(x);
            fout << Solver::solve() << "\n";
        }
    
        fin.close();
        fout.close();
        cout << " Done." << endl;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);
        
        for (int i = 1; i <= 10; ++i) {
            generate_file(i);
        }
        
        return 0;
    }
    
    • 0
      @ 2025-12-15 14:39:09

      数据生成:case5卡住太久了

      生成 Case 5 卡住的主要原因在于随机数生成的策略。

      在之前的代码中,当需要生成大量宝物(例如 count 接近 N=105N=10^5)时,使用 while (set.size() < count) set.insert(rand()) 的方式效率极低。这就好比你在集齐 100 张不同的卡片,当你手里已经有 99 张时,想抽到最后一张没重复的卡片非常难(赠券收集者问题),导致程序陷入死循环般的重试中。

      此外,针对 Case 7 这种深树/长链情况,之前的递归 DFS 找路径也可能导致栈溢出 (Stack Overflow)

      下面的版本做了两个核心优化:

      1. 优化撒点算法:使用 std::shuffle 洗牌算法,O(N)O(N) 时间内完成随机取点,无论取多少个都瞬间完成。
      2. 优化路径查找:将递归 DFS 改为 BFS + 路径回溯,防止深树导致程序崩溃。

      请使用这份优化后的代码:

      /**
       * P11249 [GESP202409 七级] 小杨寻宝 - 高性能数据生成器
       * 
       * 优化说明:
       * 1. 修复 Case 5 卡顿:使用 shuffle 洗牌算法替代 set 碰撞采样,解决 N 接近 10^5 时的死循环问题。
       * 2. 修复 Case 7 崩溃:将递归找路径改为 BFS 迭代,防止长链导致栈溢出。
       */
      
      #include <iostream>
      #include <vector>
      #include <string>
      #include <algorithm>
      #include <fstream>
      #include <random>
      #include <ctime>
      #include <queue>
      #include <set>
      #include <numeric> // for std::iota
      
      using namespace std;
      
      // ==========================================
      // Part 1: 标准题解 Solver (用于生成 .out)
      // ==========================================
      
      namespace Solver {
          const int MAXN = 100005;
          vector<int> adj[MAXN];
          bool has_treasure[MAXN];
          vector<int> treasure_nodes;
          int distU[MAXN], distV[MAXN];
          int n;
      
          void init(int _n) {
              n = _n;
              for (int i = 1; i <= n; ++i) {
                  adj[i].clear();
                  has_treasure[i] = false;
              }
              treasure_nodes.clear();
          }
      
          void add_edge(int u, int v) {
              adj[u].push_back(v);
              adj[v].push_back(u);
          }
      
          void set_treasure(int u) {
              if (!has_treasure[u]) {
                  has_treasure[u] = true;
                  treasure_nodes.push_back(u);
              }
          }
      
          int bfs(int start, int* dist) {
              for (int i = 1; i <= n; ++i) dist[i] = -1;
              queue<int> q;
              q.push(start);
              dist[start] = 0;
              
              int max_dist = -1;
              int farthest_node = start;
              
              while (!q.empty()) {
                  int u = q.front();
                  q.pop();
                  
                  if (has_treasure[u]) {
                      if (dist[u] > max_dist) {
                          max_dist = dist[u];
                          farthest_node = u;
                      }
                  }
                  
                  for (int v : adj[u]) {
                      if (dist[v] == -1) {
                          dist[v] = dist[u] + 1;
                          q.push(v);
                      }
                  }
              }
              return farthest_node;
          }
      
          string solve() {
              if (treasure_nodes.size() <= 1) return "Yes";
              int U = bfs(treasure_nodes[0], distU);
              int V = bfs(U, distU);
              bfs(V, distV);
              
              int path_len = distU[V];
              for (int t : treasure_nodes) {
                  if (distU[t] + distV[t] != path_len) return "No";
              }
              return "Yes";
          }
      }
      
      // ==========================================
      // Part 2: 数据生成器 Generator
      // ==========================================
      
      mt19937 rng(time(0));
      
      int random_int(int l, int r) {
          if (l > r) return l;
          return uniform_int_distribution<int>(l, r)(rng);
      }
      
      struct TestCase {
          int n;
          vector<pair<int, int>> edges;
          vector<int> treasures;
      };
      
      // 生成树结构
      vector<pair<int, int>> gen_tree(int n, string type) {
          vector<pair<int, int>> edges;
          if (type == "line") {
              for (int i = 2; i <= n; ++i) edges.push_back({i - 1, i});
          } 
          else if (type == "star") {
              for (int i = 2; i <= n; ++i) edges.push_back({1, i});
          } 
          else { // random or deep
              for (int i = 2; i <= n; ++i) {
                  int parent = random_int(1, i - 1);
                  if (type == "deep" && i > 5) parent = random_int(max(1, i - 5), i - 1); 
                  edges.push_back({parent, i});
              }
          }
          return edges;
      }
      
      // 优化:使用 BFS 找路径,避免递归爆栈
      vector<int> get_path_bfs(int n, int start, int end, const vector<vector<int>>& adj) {
          vector<int> parent(n + 1, 0);
          vector<bool> visited(n + 1, false);
          queue<int> q;
          
          q.push(start);
          visited[start] = true;
          
          bool found = false;
          while(!q.empty()) {
              int u = q.front();
              q.pop();
              if(u == end) {
                  found = true;
                  break;
              }
              for(int v : adj[u]) {
                  if(!visited[v]) {
                      visited[v] = true;
                      parent[v] = u;
                      q.push(v);
                  }
              }
          }
          
          vector<int> path;
          if(found) {
              int curr = end;
              while(curr != 0) {
                  path.push_back(curr);
                  if(curr == start) break;
                  curr = parent[curr];
              }
          }
          return path;
      }
      
      // 生成宝物分布
      vector<int> gen_treasures(int n, const vector<pair<int, int>>& edges, string type) {
          vector<int> t_nodes;
          
          // 构建临时邻接表
          vector<vector<int>> local_adj(n + 1);
          for (auto& e : edges) {
              local_adj[e.first].push_back(e.second);
              local_adj[e.second].push_back(e.first);
          }
      
          if (type == "all_yes") { 
              int u = random_int(1, n);
              int v = random_int(1, n);
              // 使用 BFS 获取路径上的点
              vector<int> path = get_path_bfs(n, u, v, local_adj);
              
              if (!path.empty()) {
                  int count = random_int(1, path.size());
                  // 简单的 shuffle 取点
                  shuffle(path.begin(), path.end(), rng);
                  for(int k=0; k<count; ++k) t_nodes.push_back(path[k]);
              } else {
                  t_nodes.push_back(1);
              }
          } 
          else if (type == "force_no") {
              int center = -1;
              vector<int> candidates;
              for(int i=1; i<=n; ++i) if(local_adj[i].size() >= 3) candidates.push_back(i);
              
              if (!candidates.empty()) {
                  center = candidates[random_int(0, candidates.size()-1)];
                  t_nodes.push_back(center);
                  int branch_count = 0;
                  for(int neighbor : local_adj[center]) {
                      if(branch_count >= 3) break;
                      t_nodes.push_back(neighbor);
                      branch_count++;
                  }
              } else {
                  // 没有度数>=3的点,退化为随机取 3 个点
                  vector<int> pool(n);
                  iota(pool.begin(), pool.end(), 1);
                  shuffle(pool.begin(), pool.end(), rng);
                  int count = min(n, 3);
                  for(int i=0; i<count; ++i) t_nodes.push_back(pool[i]);
              }
          }
          else { // random
              // 优化:使用 shuffle 避免 while 循环死锁
              int count = random_int(1, min(n, 20)); 
              // 20% 概率生成大量宝物
              if (random_int(1, 10) > 8) count = random_int(1, n);
              
              vector<int> pool(n);
              iota(pool.begin(), pool.end(), 1); // 填充 1, 2, ..., n
              shuffle(pool.begin(), pool.end(), rng); // 洗牌
              
              for(int i=0; i<count; ++i) t_nodes.push_back(pool[i]);
          }
          
          // 保证至少一个宝物
          if (t_nodes.empty()) t_nodes.push_back(1);
          
          return t_nodes;
      }
      
      void generate_file(int file_id) {
          string in_name = to_string(file_id) + ".in";
          string out_name = to_string(file_id) + ".out";
          ofstream fin(in_name);
          ofstream fout(out_name);
      
          int T = 10;
          if (file_id > 2) T = 5; 
          
          fin << T << endl;
          cout << "Generating Case " << file_id << " (" << T << " tests)... ";
      
          for (int t = 0; t < T; ++t) {
              int n;
              string tree_type = "random";
              string treasure_type = "random";
      
              if (file_id == 1) { n = random_int(2, 5); }
              else if (file_id == 2) { n = 5; tree_type = (t < 5) ? "line" : "star"; }
              else if (file_id == 3) { n = random_int(500, 1000); }
              else if (file_id == 4) { n = random_int(800, 1000); treasure_type = "all_yes"; }
              else if (file_id == 5) { 
                  n = random_int(50000, 100000); 
                  tree_type = "line"; 
                  treasure_type = "random"; // 这里曾是卡顿点
              }
              else if (file_id == 6) { 
                  n = random_int(50000, 100000); 
                  tree_type = "star"; 
                  treasure_type = (t % 2 == 0) ? "force_no" : "random"; 
              }
              else if (file_id == 7) { 
                  n = random_int(80000, 100000); 
                  tree_type = "deep"; 
                  treasure_type = "all_yes"; 
              }
              else if (file_id == 8) { 
                  n = random_int(80000, 100000); 
                  treasure_type = "force_no"; 
              }
              else if (file_id == 9) { 
                  n = 100000; 
                  if (t < 3) treasure_type = "all_yes";
                  else if (t < 6) treasure_type = "force_no";
              }
              else { // Case 10
                  n = 100000;
                  if (t == 0) { // 极简 Case: 1个宝物
                      fin << n << "\n";
                      for(int i=1; i<=n; ++i) fin << (i==1) << (i==n?"":" ");
                      fin << "\n";
                      vector<pair<int,int>> edges = gen_tree(n, "random");
                      for(auto& e : edges) fin << e.first << " " << e.second << "\n";
                      Solver::init(n);
                      for(auto& e : edges) Solver::add_edge(e.first, e.second);
                      Solver::set_treasure(1);
                      fout << Solver::solve() << "\n";
                      continue;
                  }
              }
      
              vector<pair<int, int>> edges = gen_tree(n, tree_type);
              vector<int> treasures = gen_treasures(n, edges, treasure_type);
      
              fin << n << "\n";
              
              // 优化输出:不需要开大数组,直接判断集合
              set<int> t_set(treasures.begin(), treasures.end());
              for (int i = 1; i <= n; ++i) {
                  fin << (t_set.count(i) ? 1 : 0) << (i == n ? "" : " ");
              }
              fin << "\n";
              
              for (auto& e : edges) {
                  fin << e.first << " " << e.second << "\n";
              }
      
              Solver::init(n);
              for (auto& e : edges) Solver::add_edge(e.first, e.second);
              for (int x : treasures) Solver::set_treasure(x);
              fout << Solver::solve() << "\n";
          }
      
          fin.close();
          fout.close();
          cout << "Done." << endl;
      }
      
      int main() {
          ios::sync_with_stdio(false);
          cin.tie(0);
          
          for (int i = 1; i <= 10; ++i) {
              generate_file(i);
          }
          
          return 0;
      }
      
      • 0
        @ 2025-12-15 14:34:57

        这是一个功能完备的C++数据生成器。它整合了标准题解逻辑(用于生成正确答案)和多样化的数据生成策略(覆盖各类测试场景),会自动生成 1.in/1.out10.in/10.out

        🛠️ 功能特点

        1. 覆盖题意:生成的数据严格遵循题目规则(树结构、宝物分布)。
        2. 多级难度
          • Subtask 1 (N5N \le 5):穷举小图、边界情况。
          • Subtask 2 (N1000N \le 1000):中等规模随机树、链状、星状图。
          • Subtask 3 (N105N \le 10^5):极限数据,包含精心构造的“必Yes”路径数据和“必No”分叉数据。
        3. 构造策略
          • Random Tree:标准的随机父节点生成法。
          • Line (链):测试必然有解的情况。
          • Star (菊花图):测试分支判定逻辑。
          • Force Yes:先随机选路径,再只在路径上放宝物。
          • Force No:强制在三个不同的子树分支上放宝物。

        📄 Generator 代码

        /**
         * P11249 [GESP202409 七级] 小杨寻宝 - 数据生成器
         * 
         * 包含:
         * 1. Solver: 标准题解逻辑,用于生成 .out
         * 2. Generator: 针对不同测试点生成 .in
         * 
         * 使用方法:编译并运行,当前目录下生成 1.in/out ~ 10.in/out
         */
        
        #include <iostream>
        #include <vector>
        #include <string>
        #include <algorithm>
        #include <fstream>
        #include <random>
        #include <ctime>
        #include <queue>
        #include <set>
        
        using namespace std;
        
        // ==========================================
        // Part 1: 标准题解 Solver (用于生成 .out)
        // ==========================================
        
        namespace Solver {
            const int MAXN = 100005;
            vector<int> adj[MAXN];
            bool has_treasure[MAXN];
            vector<int> treasure_nodes;
            int distU[MAXN], distV[MAXN];
            int n;
        
            // 初始化函数
            void init(int _n) {
                n = _n;
                for (int i = 1; i <= n; ++i) {
                    adj[i].clear();
                    has_treasure[i] = false;
                }
                treasure_nodes.clear();
            }
        
            // 加边
            void add_edge(int u, int v) {
                adj[u].push_back(v);
                adj[v].push_back(u);
            }
        
            // 设置宝物
            void set_treasure(int u) {
                if (!has_treasure[u]) {
                    has_treasure[u] = true;
                    treasure_nodes.push_back(u);
                }
            }
        
            // BFS
            int bfs(int start, int* dist) {
                for (int i = 1; i <= n; ++i) dist[i] = -1;
                queue<int> q;
                q.push(start);
                dist[start] = 0;
                
                int max_dist = -1;
                int farthest_node = start;
                
                while (!q.empty()) {
                    int u = q.front();
                    q.pop();
                    
                    if (has_treasure[u]) {
                        if (dist[u] > max_dist) {
                            max_dist = dist[u];
                            farthest_node = u;
                        }
                    }
                    
                    for (int v : adj[u]) {
                        if (dist[v] == -1) {
                            dist[v] = dist[u] + 1;
                            q.push(v);
                        }
                    }
                }
                return farthest_node;
            }
        
            // 主求解函数
            string solve() {
                if (treasure_nodes.size() <= 1) return "Yes";
                
                // 找端点 U
                int U = bfs(treasure_nodes[0], distU);
                // 找端点 V
                int V = bfs(U, distU);
                // 验证
                bfs(V, distV);
                
                int path_len = distU[V];
                for (int t : treasure_nodes) {
                    if (distU[t] + distV[t] != path_len) return "No";
                }
                return "Yes";
            }
        }
        
        // ==========================================
        // Part 2: 数据生成器 Generator
        // ==========================================
        
        mt19937 rng(time(0));
        
        int random_int(int l, int r) {
            if (l > r) return l;
            return uniform_int_distribution<int>(l, r)(rng);
        }
        
        struct TestCase {
            int n;
            vector<pair<int, int>> edges;
            vector<int> treasures;
        };
        
        // 生成树结构
        vector<pair<int, int>> gen_tree(int n, string type) {
            vector<pair<int, int>> edges;
            if (type == "line") {
                for (int i = 2; i <= n; ++i) edges.push_back({i - 1, i});
            } 
            else if (type == "star") {
                for (int i = 2; i <= n; ++i) edges.push_back({1, i});
            } 
            else { // random
                for (int i = 2; i <= n; ++i) {
                    // 稍微偏向生成深树或宽树
                    int parent = random_int(1, i - 1);
                    if (type == "deep" && i > 5) parent = random_int(i - 5, i - 1); 
                    edges.push_back({parent, i});
                }
            }
            return edges;
        }
        
        // 辅助:获取两点间路径上的所有点
        void get_path_nodes(int u, int target, int p, vector<vector<int>>& adj, vector<int>& path, bool& found) {
            path.push_back(u);
            if (u == target) {
                found = true;
                return;
            }
            for (int v : adj[u]) {
                if (v == p) continue;
                get_path_nodes(v, target, u, adj, path, found);
                if (found) return;
            }
            path.pop_back();
        }
        
        // 生成宝物分布
        vector<int> gen_treasures(int n, const vector<pair<int, int>>& edges, string type) {
            vector<int> t_nodes;
            set<int> unique_t;
            
            // 构建临时邻接表方便操作
            vector<vector<int>> local_adj(n + 1);
            for (auto& e : edges) {
                local_adj[e.first].push_back(e.second);
                local_adj[e.second].push_back(e.first);
            }
        
            if (type == "all_yes") { 
                // 构造必定 Yes 的情况:随机选路径,宝物只撒在路径上
                int u = random_int(1, n);
                int v = random_int(1, n);
                vector<int> path;
                bool found = false;
                get_path_nodes(u, v, 0, local_adj, path, found);
                
                int count = random_int(1, path.size());
                for (int k = 0; k < count; ++k) {
                    unique_t.insert(path[random_int(0, path.size() - 1)]);
                }
            } 
            else if (type == "force_no") {
                // 构造必定 No 的情况:找个度数>=3的点,向三个分支各放一个宝物
                // 如果找不到度数>=3的点(比如链),就随机撒点
                int center = -1;
                vector<int> candidates;
                for(int i=1; i<=n; ++i) if(local_adj[i].size() >= 3) candidates.push_back(i);
                
                if (!candidates.empty()) {
                    center = candidates[random_int(0, candidates.size()-1)];
                    // 找三个不同的邻居作为分支入口
                    int branch_count = 0;
                    unique_t.insert(center); // 中心也可以放
                    for(int neighbor : local_adj[center]) {
                        if(branch_count >= 3) break;
                        // 在该分支深处找一个点(简单起见直接选邻居,或者邻居的邻居)
                        unique_t.insert(neighbor);
                        branch_count++;
                    }
                } else {
                    // 退化为随机(对于链表可能是 Yes,但概率上由外部控制类型)
                    int count = min(n, random_int(3, 10));
                    while(unique_t.size() < count) unique_t.insert(random_int(1, n));
                }
            }
            else { // random
                int count = random_int(1, min(n, 20)); // 默认随机撒少量宝物
                // 特殊:有时撒很多
                if (random_int(1, 10) > 8) count = random_int(1, n);
                
                while (unique_t.size() < count) {
                    unique_t.insert(random_int(1, n));
                }
            }
            
            // 保证至少一个宝物
            if (unique_t.empty()) unique_t.insert(1);
            
            for (int x : unique_t) t_nodes.push_back(x);
            return t_nodes;
        }
        
        void generate_file(int file_id) {
            string in_name = to_string(file_id) + ".in";
            string out_name = to_string(file_id) + ".out";
            ofstream fin(in_name);
            ofstream fout(out_name);
        
            int T = 10; // 默认每组 10 个 case
            if (file_id <= 2) T = 10; // Subtask 1
            else T = 5;               // Subtask 2 & 3 稍微少一点以免文件过大
            
            fin << T << endl;
        
            for (int t = 0; t < T; ++t) {
                int n;
                string tree_type = "random";
                string treasure_type = "random";
        
                // ================= 配置策略 =================
                if (file_id == 1) { // Subtask 1: N <= 5, 基础测试
                    n = random_int(2, 5);
                    tree_type = "random";
                }
                else if (file_id == 2) { // Subtask 1: N <= 5, 边界测试
                    n = 5;
                    // 一半链(Yes),一半菊花(混合)
                    if (t < 5) tree_type = "line"; 
                    else tree_type = "star";
                }
                else if (file_id == 3) { // Subtask 2: N <= 1000, 随机
                    n = random_int(500, 1000);
                    tree_type = "random";
                }
                else if (file_id == 4) { // Subtask 2: N <= 1000, 构造 Yes
                    n = random_int(800, 1000);
                    tree_type = "random";
                    treasure_type = "all_yes";
                }
                else if (file_id == 5) { // Subtask 3: N <= 10^5, 链图 (全部应该是 Yes)
                    n = random_int(50000, 100000);
                    tree_type = "line";
                    treasure_type = "random"; 
                }
                else if (file_id == 6) { // Subtask 3: N <= 10^5, 菊花图 (中心辐射)
                    n = random_int(50000, 100000);
                    tree_type = "star";
                    // 菊花图如果不加控制随机撒点,大概率是 No
                    // 偶数个 case 强制 No,奇数个随机
                    if (t % 2 == 0) treasure_type = "force_no";
                    else treasure_type = "random";
                }
                else if (file_id == 7) { // Subtask 3: N <= 10^5, 构造必定 Yes
                    n = random_int(80000, 100000);
                    tree_type = "deep"; // 深树,更容易产生长路径
                    treasure_type = "all_yes";
                }
                else if (file_id == 8) { // Subtask 3: N <= 10^5, 构造必定 No
                    n = random_int(80000, 100000);
                    tree_type = "random";
                    treasure_type = "force_no";
                }
                else if (file_id == 9) { // Subtask 3: 混合压力测试
                    n = 100000;
                    tree_type = "random";
                    if (t < 3) treasure_type = "all_yes";
                    else if (t < 6) treasure_type = "force_no";
                    else treasure_type = "random";
                }
                else { // file_id == 10: 极限数据 + 极简边界 (1个宝物, 2个宝物)
                    n = 100000;
                    tree_type = "random";
                    // 前两个 case 测试极少宝物 (Always Yes)
                    if (t == 0) { // 1 treasure
                        vector<pair<int,int>> edges = gen_tree(n, tree_type);
                        fin << n << "\n";
                        // 输出宝物数组
                        for(int i=1; i<=n; ++i) fin << (i==1 ? 1 : 0) << (i==n?"":" ");
                        fin << "\n";
                        for(auto& e : edges) fin << e.first << " " << e.second << "\n";
                        
                        // Solver call
                        Solver::init(n);
                        for(auto& e : edges) Solver::add_edge(e.first, e.second);
                        Solver::set_treasure(1);
                        fout << Solver::solve() << "\n";
                        continue;
                    }
                }
        
                // ================= 生成数据 =================
                vector<pair<int, int>> edges = gen_tree(n, tree_type);
                vector<int> treasures = gen_treasures(n, edges, treasure_type);
        
                // ================= 写入输入文件 =================
                fin << n << "\n";
                
                // 构造宝物 0/1 数组
                vector<int> is_treasure(n + 1, 0);
                for (int x : treasures) is_treasure[x] = 1;
                
                for (int i = 1; i <= n; ++i) {
                    fin << is_treasure[i] << (i == n ? "" : " ");
                }
                fin << "\n";
                
                for (auto& e : edges) {
                    fin << e.first << " " << e.second << "\n";
                }
        
                // ================= 运行 Solver 计算输出 =================
                Solver::init(n);
                for (auto& e : edges) Solver::add_edge(e.first, e.second);
                for (int x : treasures) Solver::set_treasure(x);
                
                fout << Solver::solve() << "\n";
            }
        
            fin.close();
            fout.close();
            cout << "Generated Test Case " << file_id << endl;
        }
        
        int main() {
            ios::sync_with_stdio(false);
            cin.tie(0);
            
            // 生成 1 到 10 号测试点
            for (int i = 1; i <= 10; ++i) {
                generate_file(i);
            }
            
            return 0;
        }
        

        💡 生成策略详解

        1. 文件 1-2 (Subtask 1)

          • 1.in: 纯随机小数据,用于基本的逻辑验证。
          • 2.in: 特殊结构(纯链表、菊花图),测试算法在这些典型图上的表现。例如链表上任意宝物组合都应输出 Yes
        2. 文件 3-4 (Subtask 2)

          • 3.in: N=1000N=1000 的随机树,宝物随机分布。通常大概率是 No,用于测试“拒绝逻辑”。
          • 4.in: Force Yes 模式。先随机选一条路径,然后只在这条路径上撒点。确保算法能正确识别复杂的 Yes 情况。
        3. 文件 5-8 (Subtask 3 专项)

          • 5.in (链图)N=105N=10^5 的长链。验证大数据量下的栈深度和递归/BFS效率。答案应全为 Yes
          • 6.in (菊花图):中心节点连 105110^5-1 个叶子。强制在三个不同分支放宝物,验证是否能秒判 No
          • 7.in (构造 Yes):在 10510^5 规模的随机树上构造长路径宝物,测试大规模数据的路径判定准确性。
          • 8.in (构造 No):在 10510^5 规模随机树上寻找度数 3\ge 3 的节点构造分叉宝物,测试大规模数据的反例判定。
        4. 文件 9-10 (混合与边界)

          • 9.in: 混合模式,模拟真实的比赛测试数据分布。
          • 10.in: 包含边界情况(如只有 1 个宝物,按题意必为 Yes),以及最大规模的随机压力测试。

        ⚠️ 使用提示

        • 编译环境:代码使用了 C++11 特性(mt19937, to_string 等),请确保编译器支持(G++ 4.9+ 默认支持)。
        • 运行时间:生成 N=105N=10^5 的大规模数据时,因为要跑 BFS 计算标准答案,可能会花费几秒钟,请耐心等待直到控制台输出 "Generated Test Case 10"。
        • 0
          @ 2025-12-15 14:30:55

          这是一个符合 NOIP C++14 竞赛标准的题解代码。

          👨‍🏫 教练的解题分析

          1. 核心思路:虚树直径 + 路径判定

          题目要求“至多经过每条边一次”,意味着行走路线必须是一条简单路径。要一次性拿完所有宝物,说明所有宝物点必须分布在树上的一条单链上

          这就转化为了一个经典几何/图论问题:判断一组点集是否共线(在树上即共路径)。 这条“最长链”的两个端点,一定是宝物点集中距离最远的两个点。这类似于求树的直径。

          算法步骤

          1. 找端点 U:任选一个有宝物的点 SS,进行第一次 BFS,找到距离 SS 最远的宝物点 UU
          2. 找端点 V:从 UU 出发进行第二次 BFS,找到距离 UU 最远的宝物点 VV。此时,UUVV 确立了覆盖所有宝物的唯一可能路径的主轴。
          3. 验证:进行第三次 BFS(从 VV 出发),获取所有点到 VV 的距离。对于每一个宝物点 PP,检查是否满足:dist(U,P)+dist(P,V)==dist(U,V)dist(U, P) + dist(P, V) == dist(U, V) 如果所有宝物点都满足该条件,输出 Yes,否则输出 No

          2. 复杂度分析

          • 时间复杂度O(T×N)O(T \times N)
            • 每次查询需要做 3 次 BFS(求 U,求 V,求验证距离)。
            • BFS 的复杂度是 O(N+E)O(N+E),树上 E=N1E=N-1,所以是 O(N)O(N)
            • 数据范围 N105,T10N \le 10^5, T \le 10,总运算量约 3×1063 \times 10^6,远小于 1秒限时(约 10810^8 次运算)。
          • 空间复杂度O(N)O(N)
            • 主要用于存储邻接表 vector<int> adj[MAXN] 和距离数组。

          💻 标准题解代码 (C++14)

          /**
           * P11249 [GESP202409 七级] 小杨寻宝
           * 知识点:树的直径、BFS、路径性质
           * 
           * 核心逻辑:
           * 1. 所有宝物点必须在同一条简单路径上。
           * 2. 这条路径的两端一定是宝物点中距离最远的两个点 (U, V)。
           * 3. 验证所有宝物点是否满足 dist(U, i) + dist(i, V) == dist(U, V)。
           */
          
          #include <iostream>
          #include <vector>
          #include <queue>
          #include <algorithm>
          #include <cstring> // for memset (如果需要)
          
          using namespace std;
          
          // 定义最大节点数,稍微开大一点防止越界
          const int MAXN = 100005;
          
          // 图的邻接表
          vector<int> adj[MAXN];
          // 标记该节点是否有宝物
          bool has_treasure[MAXN];
          // 存储所有宝物点的列表,方便遍历
          vector<int> treasure_nodes;
          
          // 距离数组:distU 存到 U 的距离,distV 存到 V 的距离
          int distU[MAXN];
          int distV[MAXN];
          
          int n;
          
          /**
           * BFS 函数
           * @param start: 起点
           * @param dist:  用于存储距离结果的数组指针
           * @return:      返回距离 start 最远的【宝物节点】的编号
           */
          int bfs(int start, int* dist) {
              // 初始化距离为 -1,表示未访问
              for (int i = 1; i <= n; ++i) {
                  dist[i] = -1;
              }
              
              queue<int> q;
              q.push(start);
              dist[start] = 0;
              
              int max_dist = -1;
              int farthest_treasure_node = start; // 默认最远是自己
              
              while (!q.empty()) {
                  int u = q.front();
                  q.pop();
                  
                  // 【关键点1】在遍历过程中动态更新离起点最远的宝物点
                  // 注意:我们只关心宝物点之间的最远距离,而不是树的几何直径端点
                  if (has_treasure[u]) {
                      if (dist[u] > max_dist) {
                          max_dist = dist[u];
                          farthest_treasure_node = u;
                      }
                  }
                  
                  for (int v : adj[u]) {
                      if (dist[v] == -1) { // 如果未被访问
                          dist[v] = dist[u] + 1;
                          q.push(v);
                      }
                  }
              }
              
              return farthest_treasure_node;
          }
          
          void solve() {
              cin >> n;
              
              // 【易错点1】多组数据,必须彻底清空全局容器和状态
              for (int i = 1; i <= n; ++i) {
                  adj[i].clear();
                  has_treasure[i] = false;
              }
              treasure_nodes.clear();
              
              // 读入宝物信息
              for (int i = 1; i <= n; ++i) {
                  int a;
                  cin >> a;
                  if (a == 1) {
                      has_treasure[i] = true;
                      treasure_nodes.push_back(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);
              }
              
              // 【边界情况】如果宝物少于等于 1 个,肯定可以拿完
              if (treasure_nodes.size() <= 1) {
                  cout << "Yes" << endl;
                  return;
              }
              
              // Step 1: 从任意一个宝物点出发,找到最远的宝物点 U
              // 这里复用 distU 数组作为临时存储,只需要返回值
              int root = treasure_nodes[0];
              int U = bfs(root, distU);
              
              // Step 2: 从 U 出发,计算所有点到 U 的距离,并找到最远的宝物点 V
              int V = bfs(U, distU);
              
              // Step 3: 从 V 出发,计算所有点到 V 的距离
              bfs(V, distV);
              
              // 此时,路径 U -> V 的长度即为 distU[V]
              int path_len = distU[V];
              
              // Step 4: 验证所有宝物点是否都在 U -> V 的路径上
              bool ok = true;
              for (int t : treasure_nodes) {
                  // 【核心判定】点 t 在 U-V 路径上 <=> dist(U,t) + dist(t,V) == dist(U,V)
                  if (distU[t] + distV[t] != path_len) {
                      ok = false;
                      break;
                  }
              }
              
              if (ok) cout << "Yes" << endl;
              else cout << "No" << endl;
          }
          
          int main() {
              // 【优化建议】关闭同步流,加速大量数据的 IO
              ios::sync_with_stdio(false);
              cin.tie(0);
              
              int t;
              if (cin >> t) {
                  while (t--) {
                      solve();
                  }
              }
              return 0;
          }
          

          🔍 关键点与易错点注释

          1. 初始起点的选择

            • 第一次 BFS 的起点 root 必须从 treasure_nodes 中取(即必须是一个宝物点)。
            • 错误做法:如果从节点 1 开始 BFS,而节点 1 没有宝物且位于树的一个很偏僻的分支,找到的最远点可能并不是“宝物分布区”的端点,导致后续找出的直径错误。
          2. 多测清空

            • adj 数组需要对 1n1 \dots n 进行 clear()
            • has_treasure 数组同样需要重置。
            • 如果不清空,上一组数据的边会残留,导致图结构混乱(变稀疏图为稠密图或错误连边),引发 WA 或 TLE。
          3. 直径的定义微调

            • 通常树的直径是指树上任意两点最远距离。
            • 本题需要的是**“包含所有宝物点的最小连通子图(虚树)的直径”**。
            • 因此,bfs 函数中更新 max_dist 时,加上了 if (has_treasure[u]) 的判断条件。
          4. IO 优化

            • 数据量 N=105N=10^5,如果用 cin 不加同步流关闭,可能会稍微慢一点(虽然通常不会超时,但养成好习惯很重要)。ios::sync_with_stdio(false); cin.tie(0); 是标配。
          • 0
            @ 2025-12-15 14:28:25

            你好!我是你的OI金牌教练。很高兴能带你分析这道非常有意思的图论思维题。

            这道题考察的是树上路径的性质以及树的直径相关知识的灵活运用。我们不要急着写代码,先拿出草稿纸,把解题的逻辑画出来。


            一、 核心关键词与题意解码

            1. “树” & “至多经过每条边一次”
              • 隐含意义:这告诉我们,小杨行走的路线必须是一条简单路径(不能有环,不能走回头路,不能分叉去捡宝物再回来)。
            2. “取得所有宝物”
              • 隐含意义:所有标记为 1 的节点,必须全部落在上述的那条简单路径上。
            3. 判断 Yes/No
              • 隐含意义:我们需要找到一种高效的方法来验证这些点是否共线。

            二、 预备知识点

            1. 树的基本性质:树上任意两点之间有且仅有一条简单路径。
            2. 树的直径(变形):通常指树上最远两点的距离。这里我们需要用到求直径的思想——两次 BFS/DFS 法
            3. 路径判定:判断点 XX 是否在点 UU 和点 VV 的路径上,满足条件:dist(U, X) + dist(X, V) == dist(U, V)

            三、 启发式教学:草稿纸上的推理演练

            假设我们在黑板上画图,我会这样引导你:

            1. 简单情况分析

            • 情况 A:宝物节点排成一条直线(例如 1-2-3-4,宝物在 1, 2, 4)。
              • 能不能一次拿完?。从 1 走到 4,中间经过 2。
            • 情况 B:宝物节点分叉了(例如 1-2-3-4 是主干,2 号点连着个 5 号点,宝物在 1, 4, 5)。
              • 能不能一次拿完?不能
              • 如果你从 1 走到 4,必须经过 2,但没法去 5(去了 5 就回不来 2 继续去 4 了,因为边只能走一次)。
              • 如果你从 5 走到 4,必须经过 2,但没法去 1。
            • 结论:所有宝物节点必须在一条链上。

            2. 寻找“链”的端点

            既然必须是一条链,那这条链的两个端点肯定是所有宝物里距离最远的那两个点。

            • 这不就是求**“宝物构成的虚树的直径”**吗?

            3. 算法流程推导(在草稿纸上画步骤)

            步骤一:找端点 U

            • 随便选一个有宝物的点(比如第一个读入的宝物点 SS)。
            • 在整棵树上跑 BFS,找到距离 SS 最远的宝物点。记为 UU
            • 思考UU 一定是最终路径的一个端点(或者是端点的候选)。

            步骤二:找端点 V

            • UU 出发,再跑一遍 BFS,找到距离 UU 最远的宝物点。记为 VV
            • 结论:如果存在一条路径能覆盖所有宝物,那么这条路径的起点和终点一定是 UUVV

            步骤三:验证中间点

            • 现在我们确定了最长链 UVU \to V
            • 我们需要检查:是不是所有的宝物点都在 UVU \to V 这条路径上?
            • 验证公式:对于任意宝物点 TT,如果 dist(U, T) + dist(T, V) == dist(U, V),说明 TT 在路径上。如果不相等,说明 TT 在分叉上,输出 No

            4. 复杂度分析

            • 我们需要跑几次 BFS?
              1. 从任意宝物 SSUU
              2. UUVV(顺便求出所有点到 UU 的距离 distU)。
              3. VV 跑 BFS(求出所有点到 VV 的距离 distV)。
            • 总共 3 次 BFS。
            • 时间复杂度:O(3×N)O(3 \times N)。对于 N=105N=10^5T=10T=10 的数据,完全可以接受。
            • 空间复杂度:O(N)O(N) 存图和距离数组。

            四、 读题关键词总结

            做这类题,眼睛要抓几个词:

            1. “边至多经过一次” \rightarrow 简单路径。
            2. “树” \rightarrow 路径唯一性,距离性质。
            3. “覆盖特定点集” \rightarrow 通常涉及求点集内的最远点对(直径),或者最近公共祖先(LCA)性质。

            五、 题解标准代码 (C++14)

            #include <iostream>
            #include <vector>
            #include <queue>
            #include <algorithm>
            
            using namespace std;
            
            const int MAXN = 100005;
            const int INF = 0x3f3f3f3f;
            
            // 全局变量,避免栈溢出,方便清空
            vector<int> adj[MAXN];
            int has_treasure[MAXN]; // 标记是否是宝物点
            vector<int> treasures;  // 存储所有宝物点的编号
            int distU[MAXN];        // 存储到端点 U 的距离
            int distV[MAXN];        // 存储到端点 V 的距离
            int n;
            
            // BFS 函数:计算从 start 节点到树上所有节点的距离,结果存入 dist_array
            // 返回值:距离 start 最远的那个【宝物节点】的编号
            int bfs(int start, int* dist_array) {
                // 初始化距离数组
                for (int i = 1; i <= n; ++i) {
                    dist_array[i] = -1;
                }
                
                queue<int> q;
                q.push(start);
                dist_array[start] = 0;
                
                int max_dist = -1;
                int farthest_treasure = start;
                
                while (!q.empty()) {
                    int u = q.front();
                    q.pop();
                    
                    // 如果当前点是宝物点,更新最远宝物记录
                    if (has_treasure[u]) {
                        if (dist_array[u] > max_dist) {
                            max_dist = dist_array[u];
                            farthest_treasure = u;
                        }
                    }
                    
                    for (int v : adj[u]) {
                        if (dist_array[v] == -1) { // 未访问过
                            dist_array[v] = dist_array[u] + 1;
                            q.push(v);
                        }
                    }
                }
                return farthest_treasure;
            }
            
            void solve() {
                cin >> n;
                
                // 清空上一轮的数据
                treasures.clear();
                for (int i = 1; i <= n; ++i) {
                    adj[i].clear();
                    has_treasure[i] = 0;
                }
                
                // 读入宝物分布
                for (int i = 1; i <= n; ++i) {
                    int a;
                    cin >> a;
                    if (a == 1) {
                        has_treasure[i] = 1;
                        treasures.push_back(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);
                }
                
                // 特判:如果没有宝物或只有1个宝物,肯定 Yes
                if (treasures.size() <= 1) {
                    cout << "Yes" << endl;
                    return;
                }
                
                // 步骤 1: 从任意一个宝物点 (treasures[0]) 出发,找最远的宝物点 U
                // 这里借用 distU 数组暂存距离,其实只需要返回值
                int U = bfs(treasures[0], distU);
                
                // 步骤 2: 从 U 出发,找最远的宝物点 V,并计算所有点到 U 的距离
                int V = bfs(U, distU);
                
                // 步骤 3: 从 V 出发,计算所有点到 V 的距离
                bfs(V, distV);
                
                // 路径 U -> V 的长度
                int path_len = distU[V];
                
                // 步骤 4: 检查所有宝物点是否都在 U -> V 的路径上
                // 判定条件:dist(U, T) + dist(T, V) == dist(U, V)
                bool ok = true;
                for (int t : treasures) {
                    if (distU[t] == -1 || distV[t] == -1) {
                        // 理论上连通图不会出现 -1,除非图不连通(题目保证是树,所以不连通不可能)
                        ok = false; 
                        break; 
                    }
                    
                    if (distU[t] + distV[t] != path_len) {
                        ok = false;
                        break;
                    }
                }
                
                if (ok) cout << "Yes" << endl;
                else cout << "No" << endl;
            }
            
            int main() {
                // 开启 IO 优化,防止因为输入输出慢导致 TLE
                ios::sync_with_stdio(false);
                cin.tie(0);
                
                int t;
                cin >> t;
                while (t--) {
                    solve();
                }
                return 0;
            }
            

            六、 代码细节与优化建议

            1. 关于 BFS 的起点
              • 第一次 BFS 的起点必须是某个宝物点。如果从非宝物点(如节点 1,但节点 1 没宝物)出发,可能会找到一个离根远但不在“宝物分布区域”边缘的点,导致错误的直径判定。
            2. 空间与时间
              • 时间复杂度 O(T×N)O(T \times N)T10,N105T \le 10, N \le 10^5,总计算量约 10610^6 级别,非常稳。
              • 空间使用 vector 存图,动态分配,不会浪费。
            3. 易错点
              • 多测清空adj 数组和 has_treasure 标记在每组数据开始前一定要清空。
              • 特判:虽然题目说至少有一个宝物,但代码里加上 <= 1 个宝物的判断会让逻辑更严密。

            希望这套思路能帮你彻底搞懂这类“树上路径覆盖”的问题!如果有哪里卡住了,随时在草稿纸上画个图验证一下。

            • 1

            信息

            ID
            15713
            时间
            1000ms
            内存
            32MiB
            难度
            5
            标签
            递交数
            1
            已通过
            1
            上传者