Codeforces Round #305 (Div.1) B. Mike and Feet

問題文

解法1

値の大きい方から追加していって,区間やその幅をset, multiset で管理して頑張るだけ.
実装が悲しい感じになる.

ソースコード1

#include <bits/stdc++.h>
using namespace std;

using pii = pair<int, int>;
constexpr int inf = 1e9;

int main() {
    int n;
    cin >> n;
    vector<int> a(n), as;
    for(int i = 0; i < n; ++i) {
        scanf("%d", &a[i]);
        as.push_back(a[i]);
    }
    sort(begin(as), end(as));
    as.erase(unique(begin(as), end(as)), end(as));
    for(auto& h : a) {
        h = lower_bound(begin(as), end(as), h) - begin(as);
    }

    vector<vector<int>> pos(as.size());
    for(int i = 0; i < n; ++i) {
        pos[a[i]].push_back(i);
    }

    set<pii> segs;
    multiset<int> lens; // segment length
    vector<int> ans(n, -1);

    auto erase_seg = [&](pii s) {
        segs.erase(s);
        lens.erase(lens.lower_bound(s.second - s.first + 1));
    };
    auto insert_seg = [&] (pii s) {
        segs.insert(s);
        lens.insert(s.second - s.first + 1);
    };
    for(int i = as.size() - 1; i >= 0; --i) {
        for(auto& p : pos[i]) {
            if(segs.empty()) {
                insert_seg(make_pair(p, p));
                continue;
            }
            auto it = segs.lower_bound(make_pair(p, -1));
            if(end(segs) != it && begin(segs) != it) {
                auto pre = prev(it);
                if(it->first == p + 1 && pre->second + 1 == p) { // combine two seg
                    auto s1 = *pre, s2 = *it;
                    erase_seg(s1);
                    erase_seg(s2);
                    insert_seg(make_pair(s1.first, s2.second));
                } else if(it->first == p + 1) {
                    auto s = *it;
                    erase_seg(s);
                    insert_seg(make_pair(s.first - 1, s.second));
                } else if(pre->second + 1 == p) {
                    auto s = *pre;
                    erase_seg(s);
                    insert_seg(make_pair(s.first, s.second + 1));
                } else {
                    insert_seg(make_pair(p, p));
                }
            } else if(end(segs) != it) {
                auto s = *it;
                if(s.first == p + 1) {
                    erase_seg(s);
                    insert_seg(make_pair(s.first - 1, s.second));
                } else {
                    insert_seg(make_pair(p, p));
                }
            } else {
                it = prev(it);
                auto s = *it;
                if(s.second + 1 == p) {
                    erase_seg(s);
                    insert_seg(make_pair(s.first, s.second + 1));
                } else {
                    insert_seg(make_pair(p, p));
                }
            }
        }
        auto maxi_len = *prev(end(lens));
        ans[maxi_len - 1] = max(ans[maxi_len - 1], as[i]);
    }

    for(int i = n - 1; i >= 1; --i) {
        ans[i - 1] = max(ans[i - 1], ans[i]);
    }

    for(int i = 0; i < n; ++i) {
        printf("%d\n", ans[i]);
    }
}

解法2

各要素の値が効力を持つ幅が分かればいいわけで,これは stack で実装可能.
つまり,ある要素 a[i] が左にどれだけ効力を持つか,言い換えれば j < i で初めて a[j] < a[i] となる j はなにかが分かればよい.右も同様にやる.
答えを ans[i] とすると明らかに ans は単調減少なのでいい感じにマージできて,計算量は O(n).

ソースコード2

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    vector<int> st;
    vector<int> llen(n), rlen(n);
    for(int i = 0; i < n; ++i) {
        while(!st.empty() && a[st.back()] >= a[i]) st.pop_back();
        llen[i] = (st.empty() ? i + 1 : i - st.back());
        st.push_back(i);
    }
    st.clear();
    for(int i = n - 1; i >= 0; --i) {
        while(!st.empty() && a[st.back()] >= a[i]) st.pop_back();
        rlen[i] = (st.empty() ? n - i : st.back() - i);
        st.push_back(i);
    }

    vector<int> ans(n);
    for(int i = 0; i < n; ++i) {
        ans[llen[i] + rlen[i] - 2] = max(ans[llen[i] + rlen[i] - 2], a[i]);
    }
    for(int i = n - 1; i >= 1; --i) {
        ans[i - 1] = max(ans[i - 1], ans[i]);
    }

    for(int i = 0; i < n; ++i) {
        cout << ans[i] << " \n"[i + 1 == n];
    }
}

感想

実装担当が解法1みたいなコードを書いているようじゃだめなんだよなあ.
まあ適当にかけるという利点はあるかもしれない.