SQRT Contest #04 - Tổng khoảng cách

Xem dạng PDF

Gửi bài giải

Điểm: 19,00 (OI)
Giới hạn thời gian: 3.5s
Giới hạn bộ nhớ: 1G
Input: stdin
Output: stdout

Dạng bài
Ngôn ngữ cho phép
C, C++, Java, Kotlin, Pascal, PyPy, Python, Scratch

Cho một cây có ~n~ đỉnh, ~n - 1~ cạnh. Mỗi cạnh nối hai đỉnh ~u_i, v_i~ và có trọng số là ~w_i~. Khoảng cách giữa hai đỉnh trên cây là tổng trọng số các cạnh của đường đi đơn giữa hai đỉnh. Ban đầu có ~k~ đỉnh đặc biệt ~x_1, x_2, ..., x_k~. Bạn cần tính tổng khoảng cách nhỏ nhất từ mỗi đỉnh trên cây đến một trong các đỉnh đặc biệt đó. Có ~q~ truy vấn thay đổi các đỉnh đặc biệt, mỗi truy vấn sẽ xóa ~a~ đỉnh ~y_1, y_2, ..., y_a~ khỏi tập đỉnh, sau đó thêm ~b~ đỉnh ~z_1, z_2, ..., z_b~ vào tập đỉnh. Sau mỗi truy vấn, tính tổng khoảng cách nhỏ nhất từ mỗi đỉnh trên cây đến một trong các đỉnh đặc biệt như trên.

Dữ liệu

  • Dòng đầu tiên gồm ba số nguyên ~n, k, q~ ~(1 \le k \le n \le 10^5, 0 \le q \le 400)~.
  • Dòng tiếp theo gồm ~k~ số nguyên ~x_1, x_2, ..., x_k~ ~(1 \le x_i \le n)~. Dữ liệu đầu vào đảm bảo các giá trị ~x_i~ đôi một phân biệt.
  • ~n - 1~ dòng tiếp theo, mỗi dòng gồm ba số nguyên ~u_i, v_i, w_i~ ~(1 \le u_i, v_i \le n, 1 \le w_i \le 10000)~.
  • Tiếp theo là ~q~ cặp dòng mô tả các truy vấn, mỗi cặp dòng có định dạng như sau:
    • Dòng đầu tiên gồm số nguyên dương ~a~, sau đó là ~a~ số nguyên dương ~y_1, y_2, ..., y_a~ ~(0 \le a \le 200, 1 \le y_i \le n)~. Dữ liệu đầu vào đảm bảo các giá trị ~y_i~ đôi một phân biệt và có trong tập các đỉnh đặc biệt hiện tại.
    • Dòng tiếp theo gồm số nguyên dương ~b~, sau đó là ~b~ số nguyên dương ~z_1, z_2, ..., z_b~ ~(0 \le b \le 200, 1 \le z_i \le n)~. Dữ liệu đầu vào đảm bảo các giá trị ~z_i~ đôi một phân biệt và không có trong tập các đỉnh đặc biệt hiện tại.
  • Dữ liệu đầu vào đảm bảo sau mỗi truy vấn thay đổi cây có ít nhất ~1~ đỉnh đặc biệt.

Kết quả

  • Dòng đầu tiên gồm một số nguyên là tổng khoảng cách với tập đỉnh đặc biệt ban đầu.
  • Tiếp theo là ~q~ dòng, dòng thứ ~i~ là tổng khoảng cách sau truy vấn thay đổi thứ ~i~.

Subtask

Điểm Ràng buộc bổ sung
~15~ ~v_i = u_i + 1, k = 1, a = b~ trong mọi truy vấn
~15~ ~v_i = u_i + 1~
~15~ ~n \le 2000, q = 0~
~15~ ~k = 1, q = 0~
~10~ ~k = 1, a = b~ trong mọi truy vấn
~10~ ~q = 0~
~10~ ~q \le 30~
~10~ Không có giới hạn gì thêm

Ví dụ

Ví dụ 1

Dữ liệu

5 1 1
1
1 2 3
1 3 4
2 4 3
2 5 1
0
1 2

Kết quả

17
8

Giải thích

Hình minh họa cây ban đầu.

  • Có duy nhất đỉnh ~1~ là đỉnh đặc biệt.
  • Khoảng cách từ các đỉnh ~1, 2, 3, 4, 5~ đến đỉnh ~1~ lần lượt là ~0, 3, 4, 6, 4~, tổng là ~17~.

Hình minh họa cây sau truy vấn thay đổi.

  • Có đỉnh ~1~ và ~2~ là đỉnh đặc biệt.
  • Khoảng cách từ các đỉnh ~1, 3~ đến đỉnh ~1~ lần lượt là ~0, 4~.
  • Khoảng cách từ các đỉnh ~2, 4, 5~ đến đỉnh ~2~ lần lượt là ~0, 3, 1~.
  • Tổng khoảng cách là ~8~.

Bình luận

Hãy đọc nội quy trước khi bình luận.



  • -1
    Thang12345  đã bình luận lúc 24, Tháng 11, 2025, 8:59

    include <bits/stdc++.h>

    using namespace std; using ll = long long; const ll INF = (1LL<<60);

    int main(){ ios::syncwithstdio(false); cin.tie(nullptr); int n,k,q; if(!(cin>>n>>k>>q)) return 0; // Read all remaining integers from stdin into a vector vector<long long> all; long long x; while(cin>>x) all.push_back(x);

    // We need to reconstruct: either edges-first or specials-first.
    int m = n-1;
    bool ok = false;
    vector<tuple<int,int,int>> edges;
    vector<int> specials;
    vector<pair<vector<int>, vector<int>>> queries; // each: (add list y, remove list x) but in input it's a then y_i, then b then x_i
    // Try edges-first: next 3*m numbers are edges, then k specials, then q queries
    auto try_parse = [&](bool edges_first)->bool{
        edges.clear();
        specials.clear();
        queries.clear();
        size_t idx = 0;
        if(edges_first){
            if((int)all.size() < 3*m + k) return false;
            // edges
            for(int i=0;i&lt;m;i++){
                if(idx+2 >= all.size()) return false;
                int u = (int)all[idx++], v = (int)all[idx++], w = (int)all[idx++];
                if(u&lt;1||u>n||v&lt;1||v>n) return false;
                edges.emplace_back(u-1,v-1,w);
            }
            // specials
            for(int i=0;i&lt;k;i++){
                if(idx >= all.size()) return false;
                int s = (int)all[idx++]; if(s&lt;1||s>n) return false;
                specials.push_back(s-1);
            }
            // queries
            for(int t=0;t&lt;q;t++){
                if(idx >= all.size()) return false;
                int a = (int)all[idx++]; if(a&lt;0||a>200) return false;
                vector<int> add;
                for(int i=0;i&lt;a;i++){
                    if(idx>=all.size()) return false;
                    int y = (int)all[idx++]; if(y&lt;1||y>n) return false;
                    add.push_back(y-1);
                }
                if(idx>=all.size()) return false;
                int b = (int)all[idx++]; if(b&lt;0||b>200) return false;
                vector<int> rem;
                for(int i=0;i&lt;b;i++){
                    if(idx>=all.size()) return false;
                    int z = (int)all[idx++]; if(z&lt;1||z>n) return false;
                    rem.push_back(z-1);
                }
                queries.emplace_back(add, rem);
            }
            if(idx != all.size()) return false;
            return true;
        } else {
            // specials first
            if((int)all.size() < k + 3*m) return false;
            for(int i=0;i&lt;k;i++){
                if(idx >= all.size()) return false;
                int s = (int)all[idx++]; if(s&lt;1||s>n) return false;
                specials.push_back(s-1);
            }
            for(int i=0;i&lt;m;i++){
                if(idx+2 >= all.size()) return false;
                int u = (int)all[idx++], v = (int)all[idx++], w = (int)all[idx++];
                if(u&lt;1||u>n||v&lt;1||v>n) return false;
                edges.emplace_back(u-1,v-1,w);
            }
            for(int t=0;t&lt;q;t++){
                if(idx >= all.size()) return false;
                int a = (int)all[idx++]; if(a&lt;0||a>200) return false;
                vector<int> add;
                for(int i=0;i&lt;a;i++){
                    if(idx>=all.size()) return false;
                    int y = (int)all[idx++]; if(y&lt;1||y>n) return false;
                    add.push_back(y-1);
                }
                if(idx>=all.size()) return false;
                int b = (int)all[idx++]; if(b&lt;0||b>200) return false;
                vector<int> rem;
                for(int i=0;i&lt;b;i++){
                    if(idx>=all.size()) return false;
                    int z = (int)all[idx++]; if(z&lt;1||z>n) return false;
                    rem.push_back(z-1);
                }
                queries.emplace_back(add, rem);
            }
            if(idx != all.size()) return false;
            return true;
        }
    };
    
    if(try_parse(true)) ok = true;
    else if(try_parse(false)) ok = true;
    if(!ok){
        cerr << "Không thể phân tích input. Hãy kiểm tra định dạng.\n";
        return 0;
    }
    
    // Build adjacency
    vector<vector<pair<int,int>>> g(n);
    for(auto &e: edges){
        int u,v,w; tie(u,v,w)=e;
        g[u].push_back({v,w});
        g[v].push_back({u,w});
    }
    
    // current special set mask
    vector<char> isSpecial(n, 0);
    for(int s: specials) isSpecial[s]=1;
    
    auto compute_sum = [&]()->long long{
        // multi-source dijkstra
        vector&lt;long long> dist(n, INF);
        priority_queue&lt;pair&lt;long long,int>, vector&lt;pair&lt;long long,int>>, greater&lt;pair&lt;long long,int>>> pq;
        for(int i=0;i&lt;n;i++) if(isSpecial[i]){
            dist[i]=0; pq.push({0,i});
        }
        while(!pq.empty()){
            auto [d,u]=pq.top(); pq.pop();
            if(d!=dist[u]) continue;
            for(auto [v,w]: g[u]){
                if(dist[v] > d + w){
                    dist[v] = d + w;
                    pq.push({dist[v], v});
                }
            }
        }
        long long sum=0;
        for(int i=0;i&lt;n;i++){
            // problem guarantees at least one special in every state
            sum += dist[i];
        }
        return sum;
    };
    
    // Output initial
    cout << compute_sum() << "\n";
    // Process queries in original order
    for(auto &qr : queries){
        auto add = qr.first;
        auto rem = qr.second;
        // according to original problem statement: queries are "xóa ... khỏi tập" then "thêm ... vào tập"
        // but in example input, after edges there's 0 then 1 2 => that matched a=0 then b=1 with x=2 (i.e., remove 2)
        // Original problem wording earlier: "một truy vấn thay đổi các đỉnh đặc biệt, mời truy vấn sẽ xóa a đỉnh y1..ya khỏi tập, sau đó thêm b đỉnh z1..zb vào tập"
        // Our parsing stored (add, rem) as (y list, x list) for the order we parsed. To be robust, assume parsed pair is (add, rem) where add = y_i (to add), rem = x_i (to remove).
        // So apply removals first then additions according to problem statement:
        for(int v: rem) isSpecial[v]=0;
        for(int v: add) isSpecial[v]=1;
        cout << compute_sum() << "\n";
    }
    
    return 0;
    

    }