[2022 牛客多校7 E] Ternary Search

https://codeforces.com/contest/992/problem/E

题意:
给定一个不断往尾部插入元素的序列,每次可以交换相邻两个位置。每次得到一个新序列后,问最少几次交换能变成单峰序列。

思路:
上凸和下凹在本质上是一样的。现在我们考虑对于单个序列,变成下凹的操作。

一路上可以马上知道最小值在哪儿,并且移动最小值是没有意义的,所以我们可能会想到确定这个最小值的位置,然后贪心地考虑左右两边的情况。因为存在跨过峰值的情况,所以很难维护出每个数字左移还有右移。

如果能从左右端点确定往里面走,那么数字之间的相互影响就能简单维护了。这个问题只要每次取出最大值,比较左边比它小的数字数量和右边比它小的数字数量,贪心地走少的部分就能解决了。

因为左边比特定位置少的数字数量是固定的,且把当前值移动到左右端点后,对其他值就没有影响了,所以我们只考虑插入的影响。假设当前位置 \(i\) ,峰值位置为 \(k\) ,我们当前在峰值的右边,那么我肯定选择不跨过峰值,需要的步数为 \(a_k\)\(a_i\) 中与 \(a_i\) 的逆序对数量。如果我要跨过峰值,需要的步数为 \(a_1\)\(a_i\) 中与 \(a_i\) 的逆序对数量。我们发现如果 \(i\) 还要保持在峰值右边,那么对于后续 \(j \gt i \wedge a_j \lt a_i\) 的位置,怎么样都会跨过 \(i\) 。但如果移动到左边,怎么样都不会跨过 \(i\) 。所以如果后续这样的数量足够多,我们就把 \(i\) 看成移动到峰值左边即可。

对于左边和峰值移动的情况,显然是等价于右边左移的情况产生的贡献的。那么我们用树状数组维护逆序对,用线段树做区间修改,每次把 \(0\) 的位置取出来,把这些位置移动到峰值左边即可。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 7;

int b[N], a[N];
int n;
ll ans[N];

vector<int> vec;

struct Seg {
#define ls (id << 1)
#define rs (id << 1 | 1)
#define mid ((l + r) >> 1)
    int lz[N << 2], t[N << 2];

    void init() {
        memset(lz, 0, sizeof(lz));
        memset(t, 0x3f, sizeof(t));
    }

    void down(int id) {
        if (!lz[id]) return;
        lz[ls] += lz[id];
        lz[rs] += lz[id];
        
        t[ls] += lz[id];
        t[rs] += lz[id];

        lz[id] = 0;;
        return;
    }

    void up(int id) {
        t[id] = min(t[ls], t[rs]);
    }
    
    void modify(int id, int l, int r, int ql, int qr, int v) {
        if (l > qr || r < ql) return;
        if (l >= ql && r <= qr) {
            lz[id] += v;
            t[id] += v;
            return;
        }
        down(id);
        modify(ls, l, mid, ql, qr, v );
        modify(rs, mid + 1, r, ql, qr, v);
        up(id);
    }

    void cover(int id, int l, int r, int p, int v) {
        if (l == r) {
            t[id] = v;
            return;
        }
        down(id);
        if (p <= mid) cover(ls, l, mid, p, v);
        else cover(rs, mid + 1, r, p, v);
        up(id);
    }

    void query(int id, int l, int r) {
        if (t[id] > 0) return;
        if (l == r) {
            t[id] = inf;
            vec.emplace_back(l);
            return;
        }
        down(id);
        query(ls, l, mid);
        query(rs, mid + 1, r);
        up(id);
    }
}seg;

struct Bit {
    vector<int> sum;
    int n;

    void init(int N) {
        sum.clear();
        sum.resize(N + 1);
        n = N;
    }

    void add(int i, int x) {
        while (i <= n) {
            sum[i] += x;
            i += i & (-i);
        }
    }

    int query(int i) {
        int res = 0;
        while (i) {
            res += sum[i];
            i -= i & (-i);
        }
        return res;
    }

    int queryRange(int l, int r) {
        if (l > r) return 0;
        l--;
        return query(r) - query(l);
    }
}bit[2];

void calc() {
    ll preSum = 0;
    bit[0].init(n);
    bit[1].init(n);
    seg.init();
    for (int i = 1; i <= n; ++i) {
        preSum += bit[0].queryRange(a[i] + 1, n);
        ans[i] = min(ans[i], preSum);
        
        bit[0].add(a[i], 1);
        bit[1].add(a[i], 1);
        
        seg.modify(1, 1, n, a[i], n, -1);
        seg.cover(1, 1, n, a[i], bit[1].query(a[i] - 1));

        vec.clear();
        seg.query(1, 1, n);
        for (auto &v : vec) {
            bit[0].add(v, -1);
        }
    }
}

void solve() {
    memset(ans, 0x3f, sizeof(ans));
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    for (int i = 1; i <= n; ++i) {
        a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;
    }
    
    calc();
    
    for (int i = 1; i <= n; ++i) {
        a[i] = n - a[i] + 1;
    }
    calc();
    
    for (int i = 1; i <= n; ++i) {
        cout << ans[i] << '\n';
    }
}

int main() {
#ifndef stff577
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    cout << fixed << setprecision(20);
#endif
    int t = 1;
    while (t--) solve();
    return 0;
}
posted @ 2022-08-10 09:57  stff577  阅读(73)  评论(0编辑  收藏  举报