C. To Become Max(二分)

传送门

题意

给你一个长度为 n n n的整数数组 a a a

在一次操作中,你

  • 选择一个索引 i i i,使得 1 ≤ i ≤ n − 1 1 \le i \le n - 1 1in1 a i ≤ a i + 1 a_i \le a_{i + 1} aiai+1
  • 1 1 1增加 a i a_i ai

求最多进行 k k k次这一运算后所得到的 max ⁡ ( a 1 , a 2 , … a n ) \max(a_1, a_2, \ldots a_n) max(a1,a2,an)的最大可能值。

输入

每个测试包含多个测试用例。第一行输入包含一个整数 t t t 1 ≤ t ≤ 100 1 \le t \le 100 1t100)–测试用例数。测试用例说明如下。

每个测试用例的第一行包含两个整数 n n n k k k 2 ≤ n ≤ 1000 2 \le n \le 1000 2n1000 1 ≤ k ≤ 1 0 8 1 \le k \le 10^{8} 1k108)–数组的长度 a a a和可执行的最大操作数。

每个测试用例的第二行包含 n n n个整数 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an 1 ≤ a i ≤ 1 0 8 1 \le a_i \le 10^{8} 1ai108)–数组 a a a的元素。

保证所有测试用例的 n n n之和不超过 1000 1000 1000

输出

对每个测试用例输出一个整数,即执行最多 k k k次操作后数组可能的最大值。

思路

二分出数组中每个元素的最大值,然后对这些最大值取个 m a x max max.

思路很清楚,关键是check函数怎么写.

首先我们先假设 x x x是数组中某个元素的最大值,那么我们怎么样才能用最小的代价 c n t cnt cnt才能得到它呢?用贪心的思想来说,就是保证后面的数等于前面的数减一,即 a i = a i − 1 − 1 a_{i}=a_{i-1}-1 ai=ai11,但我们要保持a数组不变,所以我们需要开一个b数组作为a数组的副本,然后依次比较一下 a i a_i ai b i b_i bi的大小,如果遇到 a i ≥ b i a_i \geq b_i aibi并且 c n t ≤ k cnt\leq k cntk时就可以返回true,否则返回false.

时间复杂度 n 2 n^2 n2 l o g n logn logn

可能有点抽象,还是举个例子吧(语文不太好)

比如 n = 9 , k = 11 , x = 8 , p o s = 4 n=9,k=11,x=8,pos=4 n=9,k=11,x=8,pos=4的时候
数组 a : a: a: 1 , 2 , 3 , 6 , 5 , 4 , 1 , 2 , 4 1 ,2 ,3 ,6 ,5 ,4 ,1, 2, 4 1,2,3,6,5,4,1,2,4
数组 b : b: b: 1 , 2 , 3 , 8 , 7 , 6 , 5 , 4 , 3 1, 2, 3, 8, 7, 6, 5, 4, 3 1,2,3,8,7,6,5,4,3
代价 : : :    0 , 0 , 0 , 2 , 2 , 2 , 4 , 2 , 0 0, 0, 0, 2, 2, 2, 4, 2, 0 0,0,0,2,2,2,4,2,0
c n t = 2 + 2 + 2 + 4 + 2 = 12 cnt=2+2+2+4+2=12 cnt=2+2+2+4+2=12
c n t > k cnt > k cnt>k,所以返回false

n = 9 , k = 11 , x = 8 , p o s = 2 n=9,k=11,x=8,pos=2 n=9,k=11,x=8,pos=2的时候
数组 a : a: a: 1 , 2 , 3 , 6 , 5 , 4 , 1 , 2 , 4 1 ,2 ,3 ,6 ,5 ,4 ,1, 2, 4 1,2,3,6,5,4,1,2,4
数组 b : b: b: 1 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 1, 8, 7, 6, 5, 4, 3, 2, 1 1,8,7,6,5,4,3,2,1
代价 : : :    0 , 6 , 4 , 0 , 0 , 0 , 0 , 0 , 0 0, 6, 4, 0, 0, 0, 0, 0, 0 0,6,4,0,0,0,0,0,0
c n t = 6 + 4 = 10 cnt=6+4=10 cnt=6+4=10
c n t ≤ k cnt\leq k cntk,所以返回true.

完整代码

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define int long long
const int N = 3e5 + 10, mod = 998244353, M = 2 * N;
typedef pair<int, int> PII;
typedef long long ll;
const int INF_MAX = 0x3f3f3f3f3f3f3f3f;
int a[N], b[N], n, k;
bool check(int x, int pos)
{
    b[pos] = x;
    for (int i = pos + 1; i <= n; i++)b[i] = b[i - 1] - 1;
    int cnt = 0;
    for (int i = pos; i <= n; i++)
    {
        if (a[i] >= b[i])
        {
            if (cnt <= k)return true;
            else return false;
        }
        cnt += b[i] - a[i];
    }
    return false;
}
void solve()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)cin >> a[i];
    int ans = *max_element(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i++)
    {
        int l = a[i], r = a[i] + k;
        while (l <= r)
        {
            int mid = (l + r) / 2;
            if (check(mid, i))
            {
                ans = max(mid, ans);
                l = mid + 1;
            }
            else r = mid - 1;
        }
    }
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T = 1;
    cin >> T;
    while (T--)solve();
    return 0;
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值