Двокольорове дерево#13061

Двокольорове дерево (Розбір)

Автор задачі:

Andrii Kholod (Connect)

Підготував:

Andrii Kholod (Connect)

Розбір:

Andrii Kholod (Connect)

Розв'язання

Припустимо, що ми додаємо нове ребро між вершинами та .

Оскільки початковий граф є деревом, утворений цикл складається з єдиного шляху між та у дереві та нового доданого ребра.

Вага нового ребра дорівнює , тому нам потрібно максимізувати лише вагу шляху між та .

З'ясуємо, коли такий шлях можна доповнити до хорошого циклу.

Кольори всіх ребер на шляху повинні чергуватися.

Також нове ребро буде сусіднім одночасно з першим і останнім ребрами шляху. Тому перше та останнє ребра шляху повинні мати однаковий колір, щоб для нового ребра можна було вибрати протилежний колір.

В alternating-шляху перше та останнє ребра мають однаковий колір тоді й лише тоді, коли кількість ребер у шляху непарна.

Отже, задача еквівалентна знаходженню шляху максимальної ваги, для якого виконуються дві умови:

  • кольори ребер на шляху чергуються;

  • кількість ребер у шляху непарна.

Вкорінимо дерево у вершині .

Для кожної вершини визначимо

dp[v][c][p]

як максимальну вагу спрямованого вниз alternating-шляху, який починається у вершині , перше ребро якого має колір , а парність кількості ребер дорівнює .

Тут c=0 означає жовтий колір, c=1 — блакитний, p=0 означає парну кількість ребер, а p=1 — непарну.

Розглянемо ребро з вершини до її сина з вагою та кольором .

Ми можемо побудувати шлях, що складається лише з цього ребра. Його довжина непарна, тому

dp[v][c][1] = \max(dp[v][c][1],w).

Також ми можемо продовжити шлях у піддереві вершини .

Оскільки кольори повинні чергуватися, перше ребро шляху, який починається у , повинно мати колір c \oplus 1.

Якщо такий шлях має парність , то після додавання ребра його парність зміниться на p\oplus 1.

Тому

dp[v][c][p\oplus 1] = \max(dp[v][c][p\oplus 1], w+dp[u][c\oplus 1][p]).

Тепер розглянемо шляхи, найвища вершина яких дорівнює .

Такий шлях може складатися лише з однієї спрямованої вниз частини. У цьому випадку його довжина повинна бути непарною, тому кандидатами на відповідь є

dp[v][0][1]

та

dp[v][1][1].

Також шлях може йти з одного піддерева через вершину в інше піддерево.

Два ребра, сусідні з , повинні мати різні кольори. Отже, одна частина починається з жовтого ребра, а інша — з блакитного.

Загальна кількість ребер у шляху повинна бути непарною, тому парності двох частин повинні відрізнятися.

Отже, маємо два можливі варіанти:

dp[v][0][0]+dp[v][1][1]

та

dp[v][0][1]+dp[v][1][0].

Беручи максимум серед усіх вершин, отримаємо відповідь.

Зауважимо, що дві частини з різними кольорами першого ребра обов'язково використовують різні ребра, що виходять з , тому вони не можуть проходити через одного й того самого сина.

Обробляємо вершини у порядку від листків до кореня.

Часова складність розв'язання — .

Просторова складність — .

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define speed ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define endl '\n'
#define pb push_back

const ll clasik = 2e5+17;
const ll INF = (1LL<<60);

struct edge {
    ll to , w , c;
};

vector<edge> g[clasik];

ll par[clasik];
ll dp[clasik][2][2];

signed main() {
    speed;

    ll n;

    cin>>n;

    for (int i = 1 ; i<n ; i++) {
        ll u , v , w;
        char c;

        cin>>u>>v>>w>>c;

        ll col = (c == 'B');

        g[u].pb({v , w , col});
        g[v].pb({u , w , col});
    }

    vector<ll> order;
    vector<ll> st;

    par[1] = 0;
    st.pb(1);

    while(!st.empty()) {
        ll v = st.back();
        st.pop_back();

        order.pb(v);

        for (auto e : g[v]) {
            if (e.to == par[v]) continue;

            par[e.to] = v;
            st.pb(e.to);
        }
    }

    for (int v = 1 ; v<=n ; v++) {
        for (int c = 0 ; c<2 ; c++) {
            for (int p = 0 ; p<2 ; p++) {
                dp[v][c][p] = -INF;
            }
        }
    }

    reverse(order.begin() , order.end());

    ll ans = 0;

    for (auto v : order) {

        for (auto e : g[v]) {
            ll u = e.to;

            if (par[u] != v) continue;

            ll c = e.c;
            ll w = e.w;

            dp[v][c][1] = max(dp[v][c][1] , w);

            for (int p = 0 ; p<2 ; p++) {
                if (dp[u][c^1][p] == -INF) continue;

                dp[v][c][p^1] =
                    max(dp[v][c][p^1] , w + dp[u][c^1][p]);
            }
        }

        ans = max(ans , dp[v][0][1]);
        ans = max(ans , dp[v][1][1]);

        if (dp[v][0][0] != -INF && dp[v][1][1] != -INF) {
            ans = max(ans , dp[v][0][0] + dp[v][1][1]);
        }

        if (dp[v][0][1] != -INF && dp[v][1][0] != -INF) {
            ans = max(ans , dp[v][0][1] + dp[v][1][0]);
        }
    }

    cout<<ans<<endl;

    return 0;
}