はまやんはまやんはまやん

hamayanhamayan's blog

New Year and Rainbow Roads [Good Bye 2017 F]

http://codeforces.com/contest/908/problem/F

数直線上にN点ある。
i番目の点はP[i]にあり、色はC[i]である。
色は赤R,青B,緑Gの3種類でP[i]は単調増加する。
この頂点内で辺をはり、赤の頂点のみを消したグラフ、青の頂点のみを消したグラフ、のどちらも全体が連結となる様な辺を張る。
点の距離を辺の重みとするとき、重みの総和の最小値は?

解法

http://codeforces.com/contest/908/submission/33792604

貪欲に作っていく。
緑を超えて辺を張る必要は内ので緑内での連結を考えていく。
ある緑とある緑の間では、以下のように辺を貼っていくのが最適。

|--R--R--R   R--R-|
G-B-B-B    B-B-B-G
|-------------------|

こうすると赤が消えても青が消えても連結を保てる。
しかし、場面によってはこのように繋いだ方がいい場合もある。

|--R--R--R--R--R-|
G-B-B-B--B-B-B-G

どちらの色を使っても連結なので、どちらが消えても連結を保てる。
緑と緑の間ではこの2通りを試し、最適な方を採用すればいい。
端の部分は

R--R---G
B---B---|

のようにやっていけばいい。
 
コーナーケースとして緑が一つもない場合がある。
この場合は全体で連結である必要は無いため、赤と青で最適に繋げばいい

  R--R--R
B--B--B--B
#define INF INT_MAX/2
typedef long long ll;
int N;
int P[301010]; char C[301010];
//---------------------------------------------------------------------------------------------------
vector<int> g;
ll ans = 0;
void corner() {
    int r = -1, b = -1;
    rep(i, 0, N) {
        if (C[i] == 'R') {
            if (0 < r) ans += P[i] - r;
            r = P[i];
        }
        if (C[i] == 'B') {
            if (0 < b) ans += P[i] - b;
            b = P[i];
        }
    }
}
//---------------------------------------------------------------------------------------------------
void pre() {
    int r = P[g[0]], b = P[g[0]];
    rrep(i, g[0] - 1, 0) {
        if (C[i] == 'R') {
            ans += r - P[i];
            r = P[i];
        }
        if (C[i] == 'B') {
            ans += b - P[i];
            b = P[i];
        }
    }
}
void post() {
    int n = g.size() - 1;
    int r = P[g[n]], b = P[g[n]];
    rep(i, g[n] + 1, N) {
        if (C[i] == 'R') {
            ans += P[i] - r;
            r = P[i];
        }
        if (C[i] == 'B') {
            ans += P[i] - b;
            b = P[i];
        }
    }
}
//---------------------------------------------------------------------------------------------------
void make(int gs, int gt) {
    vector<int> r, b;

    rep(i, gs + 1, gt) {
        if (C[i] == 'R') r.push_back(P[i]);
        else b.push_back(P[i]);
    }

    int rn = r.size();
    int bn = b.size();

    // line
    ll ans1 = INF;
    if (0 < rn and 0 < bn) {
        ans1 = 0;

        ans1 += r[0] - P[gs];
        rep(i, 0, rn - 1) ans1 += r[i + 1] - r[i];
        ans1 += P[gt] - r[rn - 1];

        ans1 += b[0] - P[gs];
        rep(i, 0, bn - 1) ans1 += b[i + 1] - b[i];
        ans1 += P[gt] - b[bn - 1];
    }
    
    // mountain
    ll ans2 = P[gt] - P[gs];

    if (rn) {
        ll left = 0, right = P[gt] - r[0];
        ll mi = right;
        rep(i, 0, rn - 1) {
            left = r[i] - P[gs];
            right = P[gt] - r[i + 1];
            mi = min(mi, left + right);
        }
        mi = min(mi, (ll)r[rn - 1] - P[gs]);
        ans2 += mi;
    }

    if (bn) {
        ll left = 0, right = P[gt] - b[0];
        ll mi = right;
        rep(i, 0, bn - 1) {
            left = b[i] - P[gs];
            right = P[gt] - b[i + 1];
            mi = min(mi, left + right);
        }
        mi = min(mi, (ll)b[bn - 1] - P[gs]);
        ans2 += mi;
    }

    ll an = min(ans1, ans2);
    ans += an;
}
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> N;
    rep(i, 0, N) cin >> P[i] >> C[i];
    rep(i, 0, N) if (C[i] == 'G') g.push_back(i);

    if (g.size() == 0) corner();
    else {
        pre();
        int n = g.size();
        rep(i, 0, n - 1) make(g[i], g[i + 1]);
        post();
    }

    cout << ans << endl;
}