Реши на c++ Задача D. Маршруты Красной поляны Имя ...

Created on: January 8, 2026

Answered using GPT-5.2 Thinking by Chat01

Question

Реши на c++ Задача D. Маршруты Красной поляны
Имя входного файла: stdin
Имя выходного файла: stdout
Ограничение по времени: 0.5 second
Ограничение по памяти: 512 megabytes
На известном курорте Красная поляна для туристов есть N привлекательных для отдыха мест,
между которыми проложено N дорожек одинаковой длины. По каждой дорожке можно переме-
щаться в любую сторону. Дорожки устроены так, что из любого места можно добраться в любое
другое, однако иногда для этого приходится делать слишком много перемещений. Прежде чем про-
кладыватьновыедорожки,Администрациякурортахочетвыяснить,сколькосуществуетразличных
путей между всеми парами мест для отдыха, расположенными друг от друга дальше всего.
Под «местами, расположенными друг от друга дальше всего,» следует понимать пары, кратчай-
шее расстояние между которыми максимально. В качестве ответа требуется посчитать суммарное
количество кратчайших путей между всеми подобными парами мест.
Формат входных данных
В первой строке находится число N (3 ⩽ N ⩽ 200 000). В каждой из следующих N строк
находятся 2 числа — номера мест отдыха, между которыми проложена соответствующая дорожка.
Гарантируется, что все дорожки соединяют разные пары мест отдыха.
Формат выходных данных
Выведите одно число — количество кратчайших путей между между всеми парами наиболее
удаленных друг от друга мест для отдыха.
Примеры
stdin stdout
6
1 2
1 3
2 4
4 3
4 5
4 6
4
1 2
1 3
1 4

Thought

Explaining unicyclic graph processing in Russian

The graph has N vertices and N edges, meaning it contains exactly one simple cycle. To find the cycle, we first remove leaf nodes. After removing the cycle edges, each cycle node becomes the root of a tree (maybe empty). For each root, we compute the maximum distance (h[r]) and the number of nodes at that distance (c[r]), as well as the tree's diameter (diamTree[r]) and the number of pairs at that diameter (waysTree[r]).

Detailing DP computation for tree and cycle diameter

I use dynamic programming on each rooted tree, keeping track of the deepest depths and counts. For the diameter through a node, it's the sum of the two deepest depths, and the count follows. For cycle diameter, the distance between two vertices on the cycle includes distances along cycle edges. The count of paths depends on symmetry. We calculate the maximum distance and count for all vertex pairs, adjusting for cycle size and orientation. For odd cycle lengths, we handle clockwise distances within half the cycle length.

Thought for 10m 14s

Answer

Граф связный, вершин N и рёбер тоже N ⇒ это унициклический граф (ровно один простой цикл).
Любой путь вне цикла лежит в деревьях, подвешенных к вершинам цикла, а неоднозначность кратчайшего пути возможна только из-за цикла (максимум 2 кратчайших пути — по двум направлениям, когда вершины “напротив” в чётном цикле).

Нужно:

  1. найти диаметр D (максимальное кратчайшее расстояние),
  2. посчитать сумму количества кратчайших путей по всем парам вершин на расстоянии D.

Ниже — решение за O(N).

cpp
#include <bits/stdc++.h> using namespace std; struct FastScanner { static const int BUFSIZE = 1 << 20; int idx = 0, size = 0; char buf[BUFSIZE]; inline char read() { if (idx >= size) { size = (int)fread(buf, 1, BUFSIZE, stdin); idx = 0; if (size == 0) return 0; } return buf[idx++]; } template <class T> bool nextInt(T &out) { char c; do { c = read(); if (!c) return false; } while (c <= ' '); bool neg = false; if (c == '-') { neg = true; c = read(); } long long val = 0; while (c > ' ') { val = val * 10 + (c - '0'); c = read(); } out = neg ? -val : val; return true; } }; struct Edge { int to, next; }; static const int MAXN = 200000; static const int OFF = MAXN + 2; int main() { FastScanner fs; int N; if (!fs.nextInt(N)) return 0; vector<int> head(N + 1, -1); vector<Edge> edges(2 * N); int ec = 0; vector<int> deg(N + 1, 0); auto addEdge = [&](int u, int v) { edges[ec] = {v, head[u]}; head[u] = ec++; edges[ec] = {u, head[v]}; head[v] = ec++; deg[u]++; deg[v]++; }; for (int i = 0; i < N; i++) { int u, v; fs.nextInt(u); fs.nextInt(v); addEdge(u, v); } // 1) Находим вершины цикла (обрезка листьев) vector<char> isCycle(N + 1, 1); deque<int> q; for (int v = 1; v <= N; v++) if (deg[v] == 1) q.push_back(v); while (!q.empty()) { int v = q.front(); q.pop_front(); isCycle[v] = 0; for (int e = head[v]; e != -1; e = edges[e].next) { int u = edges[e].to; if (isCycle[u]) { if (--deg[u] == 1) q.push_back(u); } } } // 2) Восстанавливаем порядок вершин на цикле int start = -1; for (int v = 1; v <= N; v++) if (isCycle[v]) { start = v; break; } vector<int> cycle; cycle.reserve(N); int prev = -1, cur = start; do { cycle.push_back(cur); int nxt = -1; for (int e = head[cur]; e != -1; e = edges[e].next) { int u = edges[e].to; if (isCycle[u] && u != prev) { nxt = u; break; } } prev = cur; cur = nxt; } while (cur != start); int m = (int)cycle.size(); // 3) Для каждого корня на цикле считаем: // - downDepth[root] = максимальная глубина в подвешенном дереве (0 если нет) // - downCnt[root] = количество вершин на этой глубине // - diamTree[root] = диаметр внутри подвешенного дерева // - waysTree[root] = число пар вершин на этом диаметре (в дереве путь единственный) vector<int> parent(N + 1, -2); vector<int> downDepth(N + 1, 0); vector<long long> downCnt(N + 1, 1); vector<int> compDiamLen(N + 1, 0); vector<long long> compWays(N + 1, 0); int Dtree = 0; vector<int> order; order.reserve(1024); vector<int> st; st.reserve(1024); for (int idx = 0; idx < m; idx++) { int root = cycle[idx]; order.clear(); st.clear(); st.push_back(root); parent[root] = -1; // Обход компоненты: запрещаем переходы по ребрам цикла (cycle-cycle) while (!st.empty()) { int v = st.back(); st.pop_back(); order.push_back(v); for (int e = head[v]; e != -1; e = edges[e].next) { int u = edges[e].to; if (isCycle[v] && isCycle[u]) continue; if (u == parent[v]) continue; parent[u] = v; st.push_back(u); } } int bestLen = 0; long long bestWays = 0; // DP снизу вверх по order (в обратном порядке) for (int t = (int)order.size() - 1; t >= 0; --t) { int v = order[t]; // Ветвь "сама вершина v" как лист: глубина 0, количество 1 int bestDepth = 0; long long total = 1; long long sumsq = 1; int sources = 1; int secondDepth = -1; long long secondTotal = 0; for (int e = head[v]; e != -1; e = edges[e].next) { int u = edges[e].to; if (isCycle[v] && isCycle[u]) continue; if (parent[u] == v) { int d = downDepth[u] + 1; long long c = downCnt[u]; if (d > bestDepth) { secondDepth = bestDepth; secondTotal = total; bestDepth = d; total = c; sumsq = c * c; sources = 1; } else if (d == bestDepth) { total += c; sumsq += c * c; sources++; } else if (d > secondDepth) { secondDepth = d; secondTotal = c; } else if (d == secondDepth) { secondTotal += c; } } } downDepth[v] = bestDepth; downCnt[v] = total; // Кандидат диаметра через v int candLen = -1; long long candWays = 0; if (sources >= 2) { candLen = 2 * bestDepth; candWays = (total * total - sumsq) / 2; // пары из разных ветвей максимальной глубины } else if (secondDepth >= 0) { candLen = bestDepth + secondDepth; candWays = total * secondTotal; } if (candWays > 0) { if (candLen > bestLen) { bestLen = candLen; bestWays = candWays; } else if (candLen == bestLen) { bestWays += candWays; } } } compDiamLen[root] = bestLen; compWays[root] = bestWays; Dtree = max(Dtree, bestLen); } // массивы по циклу vector<int> w(m); vector<long long> cnt(m); for (int i = 0; i < m; i++) { int v = cycle[i]; w[i] = downDepth[v]; cnt[i] = downCnt[v]; } // 4) Диаметр с использованием цикла: // максимум w[i] + w[j] + dist_cycle(i,j) auto max_window = [&](int k) -> int { if (k <= 0) return INT_MIN / 4; deque<int> dq; // индексы i, (w[i]-i) по убыванию int best = INT_MIN / 4; int limit = (m - 1) + k; for (int j = 1; j <= limit; j++) { int i = j - 1; if (i < m) { int val = w[i] - i; while (!dq.empty()) { int bi = dq.back(); if (w[bi] - bi <= val) dq.pop_back(); else break; } dq.push_back(i); } int lb = j - k; while (!dq.empty() && dq.front() < lb) dq.pop_front(); if (!dq.empty()) { int ii = dq.front(); int cand = (w[ii] - ii) + w[j % m] + j; // (w[ii]+w[j]+(j-ii)) best = max(best, cand); } } return best; }; int Dcycle; if (m & 1) { int L = m / 2; Dcycle = max_window(L); } else { int L = m / 2; int best1 = max_window(L - 1); // dist < L int best2 = INT_MIN / 4; // противоположные (dist = L) for (int i = 0; i < L; i++) best2 = max(best2, w[i] + w[i + L] + L); Dcycle = max(best1, best2); } int D = max(Dtree, Dcycle); long long ans = 0; // 5) Добавляем вклад из диаметров внутри деревьев (там путь всегда 1) if (D == Dtree) { for (int i = 0; i < m; i++) { int root = cycle[i]; if (compDiamLen[root] == D) ans += compWays[root]; } } // Подсчет числа пар на цикле с dist <= k (уникальный кратчайший путь по циклу) auto ways_window = [&](int Dval, int k) -> long long { if (k <= 0) return 0LL; vector<long long> freq(2 * MAXN + 5, 0LL); // по ключу (w[i]-i) -> сумма cnt[i] long long ways = 0; int limit = (m - 1) + k; for (int j = 0; j <= limit; j++) { if (j > 0) { int i = j - 1; if (i < m) { int key = w[i] - i; if (key >= -MAXN && key <= MAXN) freq[key + OFF] += cnt[i]; } } int rem = j - k - 1; if (rem >= 0 && rem < m) { int key = w[rem] - rem; if (key >= -MAXN && key <= MAXN) freq[key + OFF] -= cnt[rem]; } if (j == 0) continue; int t = j % m; int target = Dval - (w[t] + j); if (target < -MAXN || target > MAXN) continue; ways += cnt[t] * freq[target + OFF]; } return ways; }; // 6) Добавляем вклад диаметров, проходящих через цикл if (D == Dcycle) { if (m & 1) { int L = m / 2; ans += ways_window(D, L); // все пары учитываются один раз } else { int L = m / 2; ans += ways_window(D, L - 1); // dist < L: путь по циклу единственный // противоположные вершины: 2 кратчайших пути по циклу for (int i = 0; i < L; i++) { if (w[i] + w[i + L] + L == D) { ans += cnt[i] * cnt[i + L] * 2LL; } } } } printf("%lld\n", ans); return 0; }

Если хочешь — могу коротко пояснить на конкретном примере из условия, как именно считаются w[i], cnt[i] и почему в чётном цикле появляется множитель 2.

Share this Q&A