Автор задачі: | Андрій Холод (Connect) |
Підготовлено: | Андрій Холод (Connect) |
Розбір: | Андрій Холод (Connect) |
Розв’язання
Маємо дерево з вершин, запускаємо обхід dfs(1). У кожній вершині порядок перебору дітей дозволено обирати довільно, отже ми можемо впливати на порядок відвідування вершин, а значить і на рядок, який утвориться при виписуванні номерів вершин у порядку обходу (без роздільників).
Нехай цей рядок цифр — це . Функція дорівнює довжині найбільшого підрядка в , який складається лише з цифр та . Потрібно максимізувати , вибираючи порядок DFS.
1. Локальні характеристики вершини
Для вершини розглянемо її десятковий запис s_v.
Визначимо:
pref_v — довжина найбільшого префікса s_v, що складається тільки з цифр та ;
suff_v — довжина найбільшого суфікса s_v, що складається тільки з цифр та ;
вершина є повністю доброю, якщо pref_v = |s_v| (тобто всі цифри — або ).
Підрядок з одних не може «перестрибнути» через погану цифру всередині s_v, тому при злитті частин обходу для різних піддерев важливими є саме ці три характеристики: чи можемо зробити увесь шматок добрим, або максимальні добрі префікс/суфікс.
2. DP-стани
Зафіксуємо корінь у вершині . Для кожної вершини (під час DFS із батьком ) розглядаємо рядок S_v, який утвориться з піддерева у порядку DFS (не заходячи в ).
Заведемо:
— довжина S_v, якщо можна вибрати порядок обходу так, що весь S_v складається лише з ; якщо неможливо — ;
— максимальна довжина доброго префікса S_v серед усіх порядків обходу;
— максимальна довжина доброго суфікса S_v серед усіх порядків обходу.
Також підтримуємо глобальну відповідь ans — максимум , який можна отримати.
3. Об’єднання дітей
Нехай у вершини є діти to. Розіб’ємо їх:
добрі діти — ті, для яких \texttt{dp[to][0]} \neq -\infty (їх піддерево можна зробити повністю з );
погані — решта.
Позначимо: [ sum = pref_v + _to — добрий dp[to][0]. ] Це максимальна «добра» довжина, яку можемо гарантовано зібрати на початку, якщо спочатку відвідати вершину (її добрий префікс), а потім усі повністю добрі піддерева.
Для поганих дітей корисні: [ mx = dp[to][1], mx0 = dp[to][2] ] (максимальні префікс та суфікс серед поганих піддерев).
4. Формули переходів
Повністю добрий фрагмент: він існує тоді й лише тоді, коли повністю добра і всі діти повністю добрі: [ dp[v][0] = sum, ] інакше dp[v][0] = -\infty.
Найкращий префікс: якщо число повністю добре, можна взяти і зайти у найкращий префікс одного поганого піддерева: [ dp[v][1] = (pref_v,; sum + mx). ] Якщо не повністю добре, то глибше все одно не можна продовжити префікс через «погану» цифру всередині s_v, тому базово маємо pref_v.
Найкращий суфікс: нехай G = sum - pref_v — сума довжин усіх повністю добрих дітей. Тоді: [ dp[v][2] =
G + suff_v, якщо немає поганих дітей,
G + mx0, інакше.
]
5. Оновлення глобальної відповіді
Найкращий підрядок з може:
складатися лише з повністю добрих піддерев дітей: ;
починатися в суфіксі , пройти всі повністю добрі піддерева і зайти в префікс одного поганого: suff_v + G + mx;
починатися в одному поганому піддереві та закінчуватися в іншому, а між ними стоять усі повністю добрі: для цього перебираємо поганих дітей і тримаємо два максимуми вже бачених: pref2 (кращий суфікс) і pref1 (кращий префікс), оновлюючи: [ ans = (ans,; G + dp[to][1] + pref2), ] [ ans = (ans,; G + dp[to][2] + pref1). ]
6. Складність
Кожне ребро переглядається разів у DFS, а всі оновлення по дітях — сумарно . Отже: [ часова складність O(n), пам’ять O(n). ]
Код розв’язку
//#pragma GCC optimize("Ofast", "unroll-loops")
//#pragma GCC target("sse", "sse2", "sse3", "ssse3", "sse4")
#include <bits/stdc++.h>
#define all(a) a.begin(),a.end()
#define len(a) (int)(a.size())
#define mp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef long double ld;
template<class T>
using vec = vector<T>;
template<typename T>
bool umin(T &a, T b) {
if (b < a) {
a = b;
return true;
}
return false;
}
template<typename T>
bool umax(T &a, T b) {
if (a < b) {
a = b;
return true;
}
return false;
}
#ifdef KoRoVa
#define DEBUG for (bool _FLAG = true; _FLAG; _FLAG = false)
#define LOG(...) print(#__VA_ARGS__" ::", __VA_ARGS__) << endl
template <class ...Ts> auto &print(Ts ...ts) { return ((cerr << ts << " "), ...); }
#else
#define DEBUG while (false)
#define LOG(...)
#endif
const int max_n = 2e5 + 11, inf = 1000111222;
vector <int> edge[max_n];
int dp[max_n][3];
// 0 - full
// 1 - pref
int good (int x) {
return x == 1 || x == 7;
}
int ans = 0;
void dfs (int v, int p = -1) {
dp[v][0] = -inf;
dp[v][1] = -inf;
dp[v][2] = -inf;
int x = v + 1;
int pref = 0, suff = 0;
vector <int> cur;
while (x) {
cur.pb(x % 10);
x /= 10;
}
while (pref < len(cur) && good(cur[len(cur) - 1 - pref])) {
++pref;
}
while (suff < len(cur) && good(cur[suff])) {
++suff;
}
int full = pref == len(cur), sum = pref, mx = 0, mx0 = 0;
bool bad = false;
for (int to : edge[v]) {
if (to == p) {
continue;
}
dfs(to, v);
if (dp[to][0] == -inf) {
full = 0;
bad = true;
umax(mx, dp[to][1]);
umax(mx0, dp[to][2]);
}
else {
sum += dp[to][0];
}
}
if (full) {
dp[v][0] = sum;
}
if (pref == len(cur)) {
dp[v][1] = sum + mx;
}
umax(dp[v][1], pref);
if (!bad) {
dp[v][2] = sum - pref + suff;
}
else {
dp[v][2] = sum - pref + mx0;
}
umax(ans, sum - pref);
umax(ans, suff + sum - pref + mx);
int pref2 = 0, pref1 = 0;
for (int to : edge[v]) {
if (to == p) {
continue;
}
if (dp[to][0] != -inf) {
continue;
}
umax(ans, sum - pref + dp[to][1] + pref2);
umax(ans, sum - pref + dp[to][2] + pref1);
umax(pref2, dp[to][2]);
umax(pref1, dp[to][1]);
}
LOG(v, ans);
}
int main() {
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
ios_base::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
--u, --v;
edge[u].pb(v);
edge[v].pb(u);
}
dfs(0);
for (int i = 0; i < n; i++) {
umax(ans, dp[i][0]);
umax(ans, dp[i][1]);
umax(ans, dp[i][2]);
}
cout << ans << '\n';
}
/*
KoRoVa!
*/