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;
}

浙公网安备 33010602011771号