NOI 2026 D1T2 传送 题解报告

我们发现,如果一个距离较近,肯定是走过去,否则就传送。由于传送是随机的,所以我们只考虑当前所在的点与目标点 \(y\) 的距离即可。

钦定这个距离为 \(d\),也就是距离 \(y\) 小于等于 \(d\) 的节点都选择直接走,否则就跳。

设距离小于等于 \(d\) 的结点与 \(y\) 的距离分别为 \(d_i\),和为 \(D\),当前一共扩展了 \(k\) 个点。从当前连通块构成的集合外某个点出发的期望步数 \(T=\frac{n+D}{k}\),推导如下:

  • \(\frac{k}{n}\) 的概率传送到连通块内部,期望为 \(\frac{k}{n}\cdot\frac{D}{k}\)
  • \(\frac{n-k}{n}\) 的概率传送到连通块外,期望为 \(\frac{n-k}{n}\cdot T\)
  • 别忘了 \(+1\) 为传送的代价。

所以 \(T=1+\frac{1}{n}(D+(n-k)T)\)

这样,我们逐层加点,尝试找到最小的 \(T\)

设新加的点数为 \(\Delta k\),新增距离 \(\Delta D=\Delta k \cdot d\)\(T'=\frac{n+D'}{k'}\),若扩展更优,即 \(T'<T\),所以 \(T>d\),则扩展(注意 \(d\) 为新的 \(d\))。

接下来,我们只需要求 \(D\) 了。我们先求出 \(0\) 号点的阈值 \(d_0\)(这里其实不用二分),BFS 向外扩展,对于相邻两点,它们的 \(k\) 之差必然小于等于 \(1\),不然扩展小的那个一定更优,所以,求出 \(d_0\),然后向外扩展,尝试 \(+1/-1\) 即可。

在点分树上,向上跳祖先,统计每个祖先处的答案,也就是以当前点 \(u\)\(\text{LCA}\) 的所有距离,排序后二分出小于等于 \(d\) 的部分求前缀和。然后,我们不能允许在在 \(u\)\(y\) 子树内,走非简单路径而贡献的答案,所以需要在每次加的时候,维护一个 \(lst\) 表示上一个 \(u\),即 \(y\)\(u\) 的哪个子树内,应该减去子树内的答案。


#include <bits/stdc++.h>
using ll = long long;
using i128 = __int128;
constexpr int N = 5e5 + 5;
std::vector < ll > adj[N];
ll siz[N], dcfa[N];
bool vis[N];
ll n;
std::vector < ll > dcd[N], dcds[N], dcs[N], dcss[N];
void get_size(ll u, ll fa)
{
    siz[u] = 1;
    for (ll v : adj[u]) {
        if (v == fa || vis[v]) continue;
        get_size(v, u);
        siz[u] += siz[v];
    }
}
void get_dis(ll u, ll fa, ll dis, std::vector < ll > &vec)
{
    vec.push_back(dis);
    for (ll v : adj[u]) {
        if (v != fa && !vis[v]) {
            get_dis(v, u, dis + 1, vec);
        }
    }
}
ll get_core(ll u, ll fa)
{
    get_size(u, fa);
    ll half = siz[u] >> 1;
    bool find = 0;
    while (!find)
    {
        find = 1;
        for (ll v : adj[u])
        {
            if (v != fa && !vis[v] && siz[v] > half) {
                find = 0;
                fa = u;
                u = v;
                break;
            }
        }
    }
    vis[u] = 1;
    std::vector < ll > all = {0};
    for (ll v : adj[u]) {
        if (!vis[v])
        {
            std::vector < ll > sub;
            get_dis(v, u, 1, sub);
            std::sort(sub.begin(), sub.end());
            ll ch = get_core(v, u);
            dcfa[ch] = u;
            dcs[ch] = sub;
            for (ll i = 0; i < sub.size(); i++)
                dcss[ch].push_back((i ? dcss[ch][i - 1] : 0) + sub[i]);
            all.insert(all.end(), sub.begin(), sub.end());
        }
    }
    std::sort(all.begin(), all.end());
    dcd[u] = all;
    for (ll i = 0; i < all.size(); i++)
        dcds[u].push_back((i ? dcds[u][i - 1] : 0) + all[i]);
    return u;
}
int dfn[N * 2], pos[N], dep[N * 2], rev[N * 2], dfc;
int st[20][N * 2], lg[N * 2];

void dfs_lca(int u, int d) {
    dfn[++dfc] = u; pos[u] = dfc; dep[dfc] = d; rev[dfc] = u;
    for (int v : adj[u]) if (!pos[v]) {
        dfs_lca(v, d + 1);
        dfn[++dfc] = u; dep[dfc] = d; rev[dfc] = u;
    }
}

void init_lca() {
    for (int i = 2; i <= dfc; i++) lg[i] = lg[i >> 1] + 1;
    for (int i = 1; i <= dfc; i++) st[0][i] = i;
    for (int i = 1; (1 << i) <= dfc; i++)
        for (int j = 1; j + (1 << i) - 1 <= dfc; j++) {
            int a = st[i - 1][j], b = st[i - 1][j + (1 << (i - 1))];
            st[i][j] = dep[a] < dep[b] ? a : b;
        }
}

int lca(int x, int y) {
    int l = pos[x], r = pos[y];
    if (l > r) std::swap(l, r);
    int k = lg[r - l + 1];
    int a = st[k][l], b = st[k][r - (1 << k) + 1];
    return rev[dep[a] < dep[b] ? a : b];
}

int dist(int x, int y) {
    int l = pos[x], r = pos[y];
    if (l > r) std::swap(l, r);
    int k = lg[r - l + 1];
    int a = st[k][l], b = st[k][r - (1 << k) + 1];
    return dep[l] + dep[r] - 2 * dep[dep[a] < dep[b] ? a : b];
}

std::pair < ll, ll > query(ll y, ll lim)
{
    ll cnt = 0; ll sum = n;
    for (ll u = y, lst = -1; u != -1; lst = u, u = dcfa[u])
    {
        ll d = dist(y, u); if (d > lim) continue;
        auto & vec = dcd[u];
        ll p = std::upper_bound(vec.begin(), vec.end(), lim - d) - vec.begin() - 1;
        if (p >= 0) {
            cnt += p + 1;
            sum += dcds[u][p] + d * (p + 1);
        }

        if (lst != -1) {
            auto & sub = dcs[lst];
            ll p = std::upper_bound(sub.begin(), sub.end(), lim - d) - sub.begin() - 1;
            if (p >= 0) {
                cnt -= p + 1;
                sum -= dcss[lst][p] + d * (p + 1);
            }
        }
    }
    return {sum, cnt};
}

bool less_eq(std::pair < ll, ll > a, std::pair < ll, ll > b) {
    return (i128)a.first * b.second <= (i128)a.second * b.first;
}


bool less_than(std::pair < ll, ll > a, std::pair < ll, ll > b) {
    return (i128)a.first * b.second < (i128)a.second * b.first;
}

ll best_d[N];
std::pair < ll, ll > best_T[N];

void dfs_dp(ll u, ll fa)
{
    for (ll v : adj[u]) {
        if (v == fa) continue;

        ll d = best_d[u];
        while (d > 0 && less_eq(query(v, d - 1), query(v, d))) d--;
        while (d < n - 1 && less_than(query(v, d + 1), query(v, d))) d++;

        best_d[v] = d;
        best_T[v] = query(v, d);
        dfs_dp(v, u);
    }
}

std::vector<std::pair<long long, int>> teleport(int c, int _n, int m,
std::vector<int> u, std::vector<int> v, std::vector<int> x, std::vector<int> y)
{
    n = _n;
    for (int i = 0; i < n - 1; i++) {
        adj[u[i]].push_back(v[i]);
        adj[v[i]].push_back(u[i]);
    }
    dfs_lca(0, 0); init_lca();

    memset(dcfa, -1, sizeof dcfa); get_core(0, -1);

    int d = 0;
    while (d < n - 1 && less_than(query(0, d + 1), query(0, d))) d++;
    best_d[0] = d;
    best_T[0] = query(0, d);

    dfs_dp(0, -1);

    std::vector < std::pair < ll, int > > ans(m);
    for (int i = 0; i < m; i++) {
        int d = dist(x[i], y[i]);
        auto [num, den] = best_T[y[i]];
        if ((i128)d * den <= num) {
            ans[i] = {d, 1};
        } else {
            ll g = std::__gcd(num, den);
            ans[i] = {num / g, (int)(den / g)};
        }
    }
    return ans;
}
posted @ 2026-07-31 19:16  J1angHz  阅读(1)  评论(0)    收藏  举报