牛客周賽 Round 67 F

MGNisme發表於2024-11-11

F.小Z的樹遷移

思路

賽事沒想出來如何做,可以發現,對於一個節點u,走d步所走的最遠距離即為 深度為depthu+d且位於u的子樹之中的節點距離根節點距離的最大值 再減去節點u距離根節點的距離即為結果

當我們查詢時該如何做?

第一步,我們先給每個節點按照dfs序進行編號,這樣保證了同一子樹的節點的編號在\(l-r\)的一個連續區間內

第二步,把每個節點存進對應的深度容器中,當我們處理一個查詢時,查詢的深度即為depth[u]+d,我們找到對應深度容器,由於容器中滿足條件的點為一段連續的區間,我們可以透過二分查詢這段區間的首尾

第三步,預處理st表維護區間最大值

注意:處理二分邊界條件

牛客官方題解

程式碼

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using piii = pair<int, pii>;
using pll = pair<ll, ll>;
using plll = pair<ll, pll>;
using plii = pair<ll, pii>;
#define fi first
#define se second
const int INF = 0x3f3f3f3f;
const ull mod1 = (1ull << 61) - 1, mod2 = 1e9 + 7;
const ull base1 = 131, base2 = 13331;
// mt19937 rnd(time(0));

const int mod = 998244353;

void solve() {
    int n;
    cin >> n;
    vector<vector<pii>> g(n + 1);
    for (int i = 1; i < n; i++) {
        int a, b, w;
        cin >> a >> b >> w;
        g[a].push_back({b, w}), g[b].push_back({a, w});
    }
    vector<int> depth(n + 1), siz(n + 1);
    vector<vector<pair<int, ll>>> tups(n + 1);
    vector<ll> dist(n + 1), dfn(n + 1);
    int cnt = 0;
    auto dfs = [&](auto&& self, int u, int fa) -> void {
        dfn[u] = ++cnt;
        depth[u] = depth[fa] + 1;
        siz[u] = 1;
        for (auto [y, w] : g[u]) {
            if (y == fa)
                continue;
            dist[y] = dist[u] + w;
            self(self, y, u);
            siz[u] += siz[y];
        }
    };
    dfs(dfs, 1, 0);
    for (int i = 1; i <= n; i++) {
        tups[depth[i]].push_back({dfn[i], dist[i]});
    }
    for (int i = 1; i <= n; i++) {
        auto& v = tups[i];
        sort(v.begin(), v.end());
    }
    vector<vector<vector<ll>>> sts(n + 1);
    for (int i = 1; i <= n; i++) {
        int len = tups[i].size();
        auto& v = sts[i];
        auto& data = tups[i];
        if (len) {
            int flen = log2(len) + 1;
            v.resize(len);
            for (int j = 0; j < len; j++)
                v[j].resize(flen);
            for (int j = 0; j < flen; j++) {
                for (int k = 0; k + (1 << j) - 1 < len; k++) {
                    if (j == 0)
                        v[k][j] = data[k].se;
                    else {
                        v[k][j] = max(v[k][j - 1], v[k + (1 << j - 1)][j - 1]);
                    }
                }
            }
        }
    }
    auto get = [&](int l, int r, int dep) {
        auto& v = sts[dep];
        int k = log2(r - l + 1);
        // cout<<l<<" "<<r<<" "<<v.size()<<" "<<dep<<" "<<k<<endl;
        // cout<<v[0].size()<<endl;
        return max(v[l][k], v[r - (1 << k) + 1][k]);
    };
    int q;
    cin >> q;
    while (q--) {
        int u, d;
        cin >> u >> d;
        int dep = depth[u] + d;
        if (dep > n) {
            cout << -1 << endl;
            continue;
        }
        int bg = dfn[u], ed = dfn[u] + siz[u] - 1;
        auto& v = tups[dep];
        if (v.size() == 0) {
            cout << -1 << endl;
            continue;
        }
        int l1 = 0, r1 = v.size() - 1;
        while (l1 < r1) {
            int mid = l1 + r1 >> 1;
            if (v[mid].fi >= bg)
                r1 = mid;
            else
                l1 = mid + 1;
        }
        int l2 = 0, r2 = v.size() - 1;
        while (l2 < r2) {
            int mid = l2 + r2 + 1 >> 1;
            if (v[mid].fi <= ed)
                l2 = mid;
            else
                r2 = mid - 1;
        }
        if (l2 < l1) {
            cout << -1 << endl;
            continue;
        }
        auto tt = v.front().fi;
        if (v.size() == 1 && (tt < bg || tt > ed)) {
            cout << -1 << endl;
            continue;
        }
        cout << get(l1, l2, dep) - dist[u] << endl;
    }
}

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int _ = 1;
    // cin >> _;
    while (_--) {
        solve();
    }
    return 0;
}

相關文章