HUASOJ 1601 题解

Problem 1601. 小灰灰的连续数列

我是题目链接

首先明确一个点: 连续递增序列的价值(对答案的贡献)是最大的。然后考虑其损耗的量(对序列和的贡献),不难发现,连续递增序列的损耗量也是最大的。
所以: 我们会在不超过 m 的范围内令连续递增序列尽可能的多。(但真的是这样吗?!

重新思考: 我们为什么说连续递增序列的价值最大?
不难发现: 是因为这一段中除第一个数外,其它的每一个数都可以提供价值。
那么: 是否可以理解为因为序列在连续的递增,没有中断所以其价值最大?(回答是肯定的)

所以我们重新定义价值最大: 分段越少的,价值越大。
容易发现: 对于分段数 x ,相同的 x 不同的分段方法使其对损耗量的贡献是不同的,这时再联想到上面的第一个明确,我们可以知道分段的越均匀,其造成的损耗是越少的(很容易证明,自行论证)。

最后: 对于分段而言,容易知道若分 x 段是最优解,则当分段数大于 x 其损耗值必定可以少于 m,而分段数小于 x 其损耗值必定大于 m。
对于连续区间中,找某个特殊点,且特殊点左侧和右侧对于某个检查的答案是完全不同的时候,我们可以采取“二分查找“这一算法,大大的优化时间复杂度:O(n) --> O(logn)。

附上代码,对于其中的“保证损耗量最少”、查找区间左右端点请读者自己确定。

#include <iostream>

#define ll long long

using namespace std;

ll n, m;

ll fu(ll x){
    return x * (x + 1) / 2;
}
int main(){
    cin >> n >> m;
    if(n < 2 || m < 3 || n >= m) {
        cout << 0;
        return 0;
    }
    ll y, mid, l = 1, r = n;
    while(l < r){
        mid = (l + r) / 2;
        y = (n - n / mid * mid) * fu(n / mid + 1) + (mid - n + n / mid * mid) * fu(n / mid); 
        if(y <= m ){
            r = mid;
        } else l = mid + 1;
    }
    ll ans = n - l;
    cout << ans;
    return 0;
}

冷艳

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值