Дано дерево из n вершин. У каждой вершины есть цве...
Erstellt am: 8. Dezember 2025
Beantwortet mit GPT-5.1 Thinking von Chat01
Erstellt am: 8. Dezember 2025
Beantwortet mit GPT-5.1 Thinking von Chat01
Дано дерево из n вершин. У каждой вершины есть цвет. Нужно обработать q запросов ( v i , c i ): найти расстояние от v i до ближайшей к v i вершины цвета c i . Расстоянием между вершинами называется минимальное количество рёбер в пути между ними.
Входные данные
На первой строке число n ( 1 ≤ n ≤ 10 5 ), следующая строка содержит числа p 1 , p 2 , ..., p n - 1 . 0 ≤ p i < i . p i – отец вершины i в дереве. Далее строка с числами a 0 , a 1 , ..., a n - 1 . 0 ≤ a i < n . a i – цвет вершины i . Далее строка с числом q ( 1 ≤ q ≤ 10 5 ). Следующие q строк содержат запросы v iq i ( 0 ≤ v i < n , 0 ≤ c i < n ).
Выходные данные
Для каждого запроса выведите одно число – расстояние до ближайшей вершины нужного цвета, или - 1 , если в дереве нет вершин такого цвета.
Решения, работающие при 1≤n≤200
, будут набирать не менее 30 баллов
Примеры
Входные данные
5
0 1 1 3
1 2 3 2 1
9
0 1
0 2
0 3
1 0
2 1
2 2
3 3
3 1
4 2
Выходные данные
0 1 2 -1 2 1 2 1 1
#include <bits/stdc++.h>
using namespace std;
vector <vector<int>> g;
int n;
vector <int> col;
vector <int> vs;
vector <int> used;
vector<vector<int>> ans;
vector <map<int, int>> vec;
vector <pair<int, int>> par;
int res = 1e9;
void dfs(int v, int p) {
vs[v] = 1;
for (auto to : g[v]) {
if (to == p) {
continue;
}
if (used[to]) {
continue;
}
dfs(to, v);
vs[v] += vs[to];
}
}
pair<int, int> fnd(int sz, int v, int d, int p) {
for (auto to : g[v]) {
if (to == p) {
continue;
}
if (used[to]) {
continue;
}
if (vs[to] > sz / 2) {
return fnd(sz, to, d + 1, v);
}
}
return {v, d};
}
void f(int v, int d, int c, int p) {
if (vec[c][col[v]] >= 1) {
vec[c][col[v]] = min(vec[c][col[v]], d);
} else {
vec[c][col[v]] = d;
}
for (auto to : g[v]) {
if (to == p) {
continue;
}
if (used[to]) {
continue;
}
f(to, d + 1, c, v);
}
}
void build(int v, int prev) {
dfs(v, v);
int sz = vs[v];
auto pr = fnd(sz, v, 0, v);
int c = pr.first;
int d = pr.second;
par[c] = {prev, d + 1};
f(c, 1, c, c);
used[c] = 1;
//cout << c << "\n";
//cout << c << " " << used[c] << "\n";
for (auto to : g[c]) {
if (used[to]) {
continue;
}
//cout << to << "\n";
build(to, c);
}
}
int query(int v, int c, int d) {
if (vec[v][c] >= 1) {
res = min(res, vec[v][c] + d);
}
while (par[v].first != -1) {
d += par[v].second;
v = par[v].first;
if (vec[v][c] >= 1) {
res = min(res, vec[v][c] + d);
}
//cout << 1;
}
if (res == 1e9) {
return -1;
}
return res - 1;
}
int main() {
cin >> n;
g.resize(n);
vs.assign(n, 0);
used.assign(n, 0);
ans.resize(n);
vec.resize(n);
par.resize(n);
for (int i = 0; i < n - 1; i++) {
int p;
cin >> p;
g[p].push_back(i + 1);
g[i + 1].push_back(p);
}
col.resize(n);
for (int i = 0; i < n; i++) {
cin >> col[i];
}
build(0, -1);
/*
for (int i = 0; i < n; i++) {
for (auto k : vec[i]) {
cout << i << " " << k.first << " " << k.second << "\n";
}
}
*/
//cout << par[0].first << "\n";
int q;
cin >> q;
for (int i = 0; i < q; i++) {
int v, c;
cin >> v >> c;
cout << query(v, c, 0) << " ";
res = 1e9;
}
}
что не так в моем коде исправь и укажи на ошибки и приведи исправленный код
Главная проблема в том, что у тебя серьёзно перепутана логика центроид-декомпозиции и запросов.
Разберу по пунктам, что именно не так, а потом дам исправленный рабочий код.
par и vec заполняются только для центроидов, но query думает, что они есть для всех вершинВ build:
cpppar[c] = {prev, d + 1}; f(c, 1, c, c); used[c] = 1;
где c — очередной центроид. То есть:
par[c] задаётся только для центроидов.vec[c] тоже заполняется только для центроидов в f.Но в main:
cppcout << query(v, c, 0) << " ";
и в query:
cppint query(int v, int c, int d) { if (vec[v][c] >= 1) { // <-- v может быть НЕ центроидом res = min(res, vec[v][c] + d); } while (par[v].first != -1) { // <-- par[v] для обычных вершин не задавался d += par[v].second; v = par[v].first; if (vec[v][c] >= 1) { res = min(res, vec[v][c] + d); } } ... }
Проблемы:
par[v] и vec[v] никогда не инициализировались, но ты их читаешь → неопределённое поведение.par[v].first по умолчанию будет (0, 0), т.е. par[v].first != -1, и цикл может ходить по мусорным данным.В корректной центроид-декомпозиции:
(центроид, расстояние до этого центроида).best[centroid][color]).Запрос устроен так:
textans = min по всем (c, dist(v,c)) из списка для v: dist(v,c) + best[c][color]
А не поднятием по par[v], как у тебя.
map через operator[] как проверки существованияВ f:
cppif (vec[c][col[v]] >= 1) { vec[c][col[v]] = min(vec[c][col[v]], d); } else { vec[c][col[v]] = d; }
vec[c][col[v]]:
operator[] создаёт его со значением 0.0 как с «значения ещё не было».Из-за этого тебе пришлось:
d = 1,query потом возвращать res - 1.Это костыль. Нормально делается так:
cppauto &mp = vec[c]; int color = col[v]; auto it = mp.find(color); if (it == mp.end()) mp[color] = d; else it->second = min(it->second, d);
d с 1, res - 1 в конце)В build:
cppf(c, 1, c, c);
и в query:
cppreturn res - 1;
Ты искусственно:
Это опять же следствие того, что ты используешь 0 как «нет значения» в map. Это можно исправить аккуратной работой с map::find / count.
par используется как «родитель центроида», но для вершины v в запросе ты считаешь, что par[v] задаёт подъём по дереву центроидов → логически неверноpar в build хранит:
cpppar[c] = {prev, d + 1};
где prev — родитель центроидов в дереве центроидов. Но ты никогда не задаёшь par для обычных вершин, то есть вершину v ты не привязываешь к цепочке центроидов. Для корректной работы нужно отдельное хранилище для каждого v:
cppvector<vector<pair<int,int>>> up; // up[v] = { (centroid, dist) ... }
и использовать именно его в запросах.
Ниже — переработанный вариант с нормальной центроид-декомпозицией:
c:
best[c][color] — минимальное расстояние от c до вершины этого цвета.v:
up[v] — список (c, dist(v,c)) по всем центроидам на пути в дереве центроидов.Запрос (v, color):
cppans = min по (c, dist_v_c) из up[v], если в best[c] есть этот color: dist_v_c + best[c][color]
cpp#include <bits/stdc++.h> using namespace std; int n; vector<vector<int>> g; vector<int> col; vector<int> sz; vector<int> used; // up[v] = список (центроид, расстояние от v до этого центроида) vector<vector<pair<int,int>>> up; // best[c][color] = минимальное расстояние от центроида c до вершины цвета color vector<map<int,int>> best; void dfs_size(int v, int p) { sz[v] = 1; for (int to : g[v]) { if (to == p || used[to]) continue; dfs_size(to, v); sz[v] += sz[to]; } } int find_centroid(int v, int p, int total) { for (int to : g[v]) { if (to == p || used[to]) continue; if (sz[to] > total / 2) { return find_centroid(to, v, total); } } return v; } // Обходим компоненту от центроида c, считаем расстояния до него и обновляем best/ up void dfs_add(int v, int p, int c, int dist) { // Запомним, что вершина v на расстоянии dist от центроида c up[v].push_back({c, dist}); // Обновляем лучший ответ по цвету для центроида c auto &mp = best[c]; int color = col[v]; auto it = mp.find(color); if (it == mp.end() || dist < it->second) mp[color] = dist; for (int to : g[v]) { if (to == p || used[to]) continue; dfs_add(to, v, c, dist + 1); } } void build(int v) { dfs_size(v, -1); int c = find_centroid(v, -1, sz[v]); // нашли центроид used[c] = 1; // из центроида c обойдём его компоненту и заполним up / best dfs_add(c, -1, c, 0); // рекурсивно строим для оставшихся компонент for (int to : g[c]) { if (!used[to]) { build(to); } } } // запрос: расстояние от v до ближайшей вершины цвета color int query(int v, int color) { const int INF = (int)1e9; int ans = INF; for (auto [c, dist_to_c] : up[v]) { auto &mp = best[c]; auto it = mp.find(color); if (it != mp.end()) { ans = min(ans, dist_to_c + it->second); } } return (ans == INF ? -1 : ans); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n; g.assign(n, {}); col.resize(n); sz.assign(n, 0); used.assign(n, 0); up.assign(n, {}); best.assign(n, {}); // дерево по родителям for (int i = 0; i < n - 1; i++) { int p; cin >> p; // p — отец вершины (i+1) g[p].push_back(i + 1); g[i + 1].push_back(p); } // цвета вершин for (int i = 0; i < n; i++) { cin >> col[i]; } // строим центроид-декомпозицию build(0); int q; cin >> q; while (q--) { int v, c; cin >> v >> c; cout << query(v, c) << ' '; } return 0; }
Если хочешь, могу отдельно нарисовать картинку, как именно формируются up[v] и best[c] на простом примере дерева, чтобы было наглядно, как работают запросы.