将大于等于 的数都映射为 ,否则映射为 ,再求一个前缀和
若要求分段的中位数都大于等于 ,则必须 ,即大于等于 的数的数量需要比小于 的数量多(很好理解,对于一个小于 的数,至少需要有两个大于等于 的数来抵消它)
而此时,要求最大分段数量,就可以转化为求最长上升子序列的问题了
若要求每一段都是合法的,那么必然需要构成最长上升子序列
对于样例:
n = 8, m = 2
x: 3  1  3  3  3  1  3  3  1
a: 1 -1  1  1  1 -1  1  1 -1
s: 1  0  1  2  3  2  3  4  3
一种最优的分段方法为:
3 || 1 3 3 || 3 1 3 3
将其转化为 与 之后,对此我们只需要维护一个单调上升序列 即可,初始化为
,将其加入到 中,此时
,不可能从这里划分,不必管他
, 中没有比它大的,不管
,将其加入到 中,此时
,将其加入到 中,此时
,已经存在,不管
, 中没有比它大的,不管
,将其加入到 中,此时
,由于是最后一个元素是必选的(后续没有元素再进行抵消),所以可以通过该元素对应位置判断出应该分几段,为了使序列保持单调,找到在 中第一个大于 的位置,取第一个位置到该位置的元素个数即为分段个数。
最优就是分段后每一段的和为
看代码补题真不错
void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> s(n + 1);
    for(int i = 1, x; i <= n; i++) {
        cin >> x;
        s[i] = s[i - 1] + (x >= m ? 1 : -1);
    }
    if(s[n] <= 0) {
        cout << -1 << "\n";
        return ;
    }
    vector<int> f{0};
    for(int i = 1; i <= n; i++) {
        if(s[i] <= 0) {
            continue;
        }
        auto it = lower_bound(f.begin(), f.end(), s[i]);
        if(i == n) {
            cout << it - f.begin() << "\n";
            return ;
        }
        if(it == f.end()) {
            f.push_back(s[i]);
        }
    }
    return ;
} 

 京公网安备 11010502036488号
京公网安备 11010502036488号