洛谷P4343 [SHOI2015] 自动刷题机

35 阅读3分钟

原题:www.luogu.com.cn/problem/P43…

题面:

P4343 [SHOI2015] 自动刷题机

题目背景

曾经发明了信号增幅仪的发明家 SHTSC 又公开了他的新发明:自动刷题机——一种可以自动 AC 题目的神秘装置。

题目描述

自动刷题机刷题的方式非常简单:首先会瞬间得出题目的正确做法,然后开始写程序。每秒,自动刷题机的代码生成模块会有两种可能的结果:

  1. 写了 xx 行代码。
  2. 心情不好,删掉了之前写的 yy 行代码。(如果 yy 大于当前代码长度则相当于全部删除。)

对于一个 OJ,存在某个固定的正整数长度 nn,一旦自动刷题机在某秒结束时积累了大于等于 nn 行的代码,它就会自动提交并 AC 此题,然后新建一个文件(即弃置之前的所有代码)并开始写下一题。SHTSC 在某个 OJ 上跑了一天的自动刷题机,得到了很多条关于写代码的日志信息。他突然发现自己没有记录这个 OJ 的 nn 究竟是多少。所幸他通过自己在 OJ 上的 Rank 知道了自动刷题机一共切了 kk 道题,希望你计算 nn 可能的最小值和最大值。

输入格式

第一行两个整数 l,kl,k,表示刷题机的日志一共有 ll 行,一共切了 kk 题。

接下来 ll 行,每行一个整数 xix_i,依次表示每条日志。若 xi0x_i \geq 0,则表示写了 xix_i 行代码,若 xi<0x_i \lt 0,则表示删除了 xi-x_i 行代码。

输出格式

输出一行两个整数,分别表示 nn 可能的最小值和最大值。
如果这样的 nn 不存在,请输出一行一个整数 1-1

输入输出样例 #1

输入 #1

4 2
2
5
-3
9

输出 #1

3 7

说明/提示

数据规模与约定
  • 对于 20%20\% 的数据,保证 1l101 \le l \le 10
  • 对于 40%40\% 的数据,保证 1l1001 \le l \le 100
  • 对于 60%60\% 的数据,保证 1l2×1031 \le l \le 2 \times 10^3
  • 对于 100%100\% 的数据,保证 1l1051 \leq l \le 10^5109xi109-10^9 \le x_i \le 10^9kkint 存储范围内。

SolutionSolution

题目要求分别找到最小的 nn 和最大的 nn ,分别考虑应该如何寻找。

如果逐个对可能的 nn 进行枚举的话,显然是不可行的,因为 nn 最大的值可能达到 1e141e14 ,因此考虑优化这个选择对应 nn 的过程。

首先对于每个确定的 nn ,我们可以发现最终得到的做出的题目数是确定的,记作 cntcnt 。那么若 cnt<kcnt<k ,则说明此时 nn 的值太大了,导致少做了题;反之,若 cnt>kcnt>k ,则说明此时 nn 的值太小了,多做了题。可以看出这个函数关系是满足单调性的,所以我们可以对 nn 进行二分答案。

另一个问题:我们应该怎么确定 nn 的最小值和最大值呢?

我们考虑所有满足条件的 nn ,可以发现它们的值都是连续的。所以可以在二分答案的过程中如果碰到一个满足条件的 midmid ,就记录到 ansans 中,同时根据此时所求的是最小值还是最大值来决定是将二分区间向左挪还是向右挪。

CodingCoding

#include <iostream>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;

#define ll long long
#define ull unsigned long long
#define debug(x) cout << #x << "=" << x << "\n";

ll l, k;
const int maxl = 1e5 + 10;
ll x[maxl];
bool flag;

int check(ll n)
{
    ll cur_val = 0;
    ll cnt = 0;

    for (int i = 1; i <= l; i++)
    {
        cur_val = max(cur_val + x[i], (ll)0);

        if (cur_val >= n)
        {
            cur_val = 0;
            cnt++;
            if (cnt > k)
                return 2;
        }
    }

    return (cnt == k ? 0 : 1);
}

ll binary_min(ll left, ll right)
{
    ll mid, ans;

    while (left <= right)
    {
        mid = ((left + right) >> 1);
        int res = check(mid);

        if (res == 0)
            right = mid - 1, ans = mid, flag = true;
        else if (res == 1)
            right = mid - 1;
        else
            left = mid + 1;
    }

    return ans;
}

ll binary_max(ll left, ll right)
{
    ll mid, ans;

    while (left <= right)
    {
        mid = ((left + right) >> 1);
        int res = check(mid);

        if (res == 0)
            left = mid + 1, ans = mid, flag = true;
        else if (res == 1)
            right = mid - 1;
        else
            left = mid + 1;
    }

    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> l >> k;
    for (int i = 1; i <= l; i++)
        cin >> x[i];

    ll min_val = binary_min(1, 1e14);
    ll max_val = binary_max(1, 1e14);

    if (flag)
        cout << min_val << " " << max_val;
    else
        cout << -1;

    return 0;
}

总体时间复杂度为 O(llog(xl))O(l\cdot log(xl))