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;
}