CF1693F题解

备注

发表时间:2023-06-17 21:51

前言

yny 学长来 cdqz 讲课,写一篇讲课的题的题解纪念一下。

题意

给你一个 01 序列,有以下操作:

  • 选择一段区间
    cnt0,cnt1 分别表示该区间中 01 的数量。
    花费 |cnt0cnt1|+1 的代价对区间进行升序排序。

求最小代价。

思路

肯定是使每次操作的区间中 01 个数差越小越好,所以考虑每次都选择尽可能长01 个数差最小的区间。

如果最前面一段都是 0,那就不用管前面。相当于每次都从第一个 1 开始考虑(注意不是操作区间的左端点)。

题解

因为每次操作必须cnt0=cnt1 才最优。
所以现在考虑如何找最优区间。
为了简化思维,我们可以先只考虑整个序列 01 多时如何求解,因为这样我们可以加 0 进行贪心。

弱化版

首先每次从区间最左端的 1 开始考虑,我们可以贪心去找最优区间。
因为 0 永远比 1 多,所以如果后面的 0 少了可以直接从前面拿 0 来补齐。
所以现在问题就在如何求最优区间。

此时,本题最妙的点来了。我们可以给 01 赋值为 11,将这个区间的变化用图表示。

我们假设要找序列 Sl 的最优区间右端点 Sr,就可以在图中做一条与横轴平行的直线。

其中曲线与直线最右端的交点就是 Sr
而图中的曲线就只用记录与直线 y=i{i[1,n]iZ+} 的最右端交点。

最后,如何将方法一般化呢?

我们可以将原来 01 多的序列变成上面这种特殊序列。
因为原序列中 1 要移到后面、0 要移到前面,所以其实我们只用将原序列翻转,再给 0 1 都取反就行了(读者可自行思考)。

注意

  • 特判序列是否已为升序。

  • 01 赋值。

代码

/*
 * @Author: H.F.Y
 * @Date: 2023-06-17 16:40:55
 * @Last Modified by: H.F.Y
 * @Last Modified time: 2023-06-17 17:41:39
 */
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 5;
int n, a[N], pre_max[N];
char c[N];
signed main(){
    //freopen(,stdin);
    //freopen(,stdout);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while(T--){
        cin >> n; int cnt1 = 0, cnt0 = 0;
        memset(pre_max, 0, sizeof pre_max);
        memset(a, 0, sizeof a);
        for(int i = 1; i <= n; ++i){
            cin >> c[i]; a[i] = ((c[i] - '0') ? 1 : - 1);
            if(a[i] == 1)++cnt1;
            else ++cnt0;
        }
        bool opt = true;
        for(int i = n - 1; i; --i)if(a[i] > a[i + 1])opt = false;
        if(opt){
            cout << 0 << '\n';
            continue;
        }
        if(cnt1 == cnt0){
            cout << 1 << '\n';
            continue;
        }
        else if(cnt1 > cnt0){
            swap(cnt1, cnt0);
            for(int i = 1; i <= n; ++i)a[i] = ((c[n - i + 1] - '0') ? - 1 : 1);
        }
        for(int i = 1, sum = 0; i <= n; ++i){
            sum -= a[i];
            if(~ sum)pre_max[sum] = i;
        }
        int ans = 1, i = 0, cnt = cnt0 - cnt1;
        while(a[i + 1] == - 1 and i < n)++i;
        while(i < cnt){
            ++ans;
            int t = pre_max[i] - i + 1; t /= 2;
            i += t;
        }
        cout << ans << '\n';
    }
    return 0;
}
posted @   Lyrella  阅读(12)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示