CodeForces 830C 浅谈奇妙题

这里写图片描述
世界真的很大
这个世界上有一些题,完全和几乎任何算法扯不上关系,做的时候也很头疼,做完了会有些怀疑到底做对没有,但是会有不容置疑的成就感
听上去很怪但这样的题确实是存在着的,这样的题我管他叫奇妙题
这道题就是如此
看题先:

description

n棵竹子,初始时每棵竹子高度都是0,每棵竹子每天长高1m
对于每棵竹子,我们不希望其高度超过a[i],如果超过了,我们就会把超过的部分减去
奇怪的是减去之后竹子就不会再长了
我们不希望每天去看一下竹子的情况,希望每隔d天去看一下竹子的情况
本着爱护环境的原则,我们不希望减去的竹子长度之和大于K
我们最多可以隔多少天去看一次竹子?

input

第一行两个整数nK
第二行n隔整数表示a[i]

output

一个整数表示答案,即最大的d值

首先看题,我不信你的第一感觉不是二分,反正我是二分
然后我就想,太简单了吧,直到最后才发现不是二分,但为时已晚,只能打个暴力,20分
至于为什么不能二分呢,因为答案没有单调性,有可能d取4时剪掉的长度比取3时的少,就没法二分了
首先考虑暴力,从1到a的最大值枚举d,O(n)check,取一个最大值
考虑每次check,把每个a除以d向上取整再乘以d在减去a,计入减去的长度,即:
sigma i : ([a[i] /d ]*d-a[i])< = K
这样我们得到了这一个方程
考虑优化暴力
对于完整数据,a的级别是10^9,枚举是不可能的了,而且每次check需要把每一个a跑一遍计算答案是必须的了,再者n的值一共不超过100,比较小,优化n的可能性较低,所以应该考虑怎么去优化枚举d的情节
首先,为了考虑优化d,我们需要把d从原方程里独立出来,不然和a一起枚举的话很难想到优化办法
也就是一个简单的不等式变形而已
原始等价于:
d * sigma i :[a[i]/d] <= K+ sigma i : a[i]
考虑d存在于等式的左边,等式一共有3项,只要知道了其中两项,就能通过不等式移项得到第三项的最大值,而且等式右边是一个常量,就意味着我们只需要用可以接受的复杂度知道等式左边两项里的任意一项,就可以得到一个含有d的式子的最大值,进而得到d的最大值,即答案
考虑左边第二项:sigma i :[a[i]/d] ,对于一个a[i]来讲,由于有“取整”这个误差,所以对于一段区间里的d来讲a[i]/d的值是相同的,换句话说,就算得到了a[i]/d的最值,也不太好得到d的最值
而反观第一项,d,简单明了,这玩意儿的最值就是答案
那么问题就很简单了,在d和sigma : i a[i]/d之间,我们需要预处理后者,得到前者的答案。
思路一步步明了了
现在考虑怎么预处理后者的答案。
首先我们想到,对于一段区间的a[i]/d的值很可能是相同的,那么就是说,a[i]/d的值不会有很多,起码不会有a[i]个,应该是只有a[i]^0.5个不同的取值
那么我们就优先把这样不同的a[i]/d的d保存下来
每次枚举d的时候就直接枚举使得这样的取值不同的d,也就是保存下来的d,只有根号级别了
然后用右边的值除以这个值就能得到一个最大的d值,检查求出的d值是不是在这个枚举的d值所要求的范围内,也就是比枚举的那个大,来更新答案
有几点需要注意
求出来的d的数组可以去一下重,减少运算量,stl里的unique函数可以捡现成的
完整代码:

#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long dnt;

int n,cnt=0;
dnt C=0,K,ans,a[500010],va[50000010];

int main()
{
    cin >> n >> K ;
    for(int i=1;i<=n;i++)
    {
        cin >> a[i];
        C+=a[i];
        for(int j=1;j*j<=a[i];j++)
            va[++cnt]=j,va[++cnt]=(a[i]-1)/j+1; 
    }
    C+=K,ans=1;
    sort(va+1,va+cnt+1);
    cnt=unique(va+1,va+cnt+1)-(va+1);
    for(register int i=1;i<=cnt;i++)
    {
        dnt now=0;
        for(int j=1;j<=n;j++)
            now+=(a[j]-1)/va[i]+1;
        if(C/now>=va[i]) ans=C/now;
    }
    cout << ans << endl ;
    return 0;
}
/*
EL PSY CONGROO
*/

嗯,就是这样

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值