洛谷P3606 [USACO17JAN]Building a Tall Barn建谷仓

感觉和吃了屎一样,很难受;
做了做了一天就做了一道题目,现在才调出来,明天要考化学,感觉自己跟个傻逼一样;
我知道我自己傻逼的原因,我真的受够了;
另外,noip之后,如果我没有退役,我一定要想尽办法搭建自己的博客;
但愿吧;
https://daniu.luogu.org/problem/show?pid=3606
关于做法的话,洛谷的题解讲的还是比较清楚的

令 ti 表示 第i层多一头牛的改变量
ti = a[i]/ci*(ci+1)
最终状态中 ti一定是很接近的,否则可以从j拿一头牛到i去,使得结果更优
所以我们就二分 ti
ti = a[i]/ci*(ci+1) —>ci*ci+ci-ai/ti=0 反解出ci,然后对ci求和,如果不到k-n则可以缩小上界,否则提高下界

这个贪心,虽然并不会很严谨的证明,但是还是很显然的;
简单来说
100/50和2/1的值都是2
但是我们这个时候可以给分母+1,显然会去加在2上面;
对于上面那个ci
我们要明确知道ci代表什么;
ci指的是当前我们对ai放的奶牛个数;
所以这个ci在最后用一元二次方程求解的时候我们是要向上取整数的;
然后还有一个最关键的地方,就是我们虽然二分了这么一个t
但是这并不代表我们把所有的k头奶牛都用上了,所以我们最后还需要吧这k头奶牛的贡献减去,那么他猛的贡献是多少呢,就是我们二分的这个t

#include<bits/stdc++.h>
#define Ll long long
using namespace std;
const Ll N=1e5+5;
double a[N],l,r,mid,ans,T;
Ll x,k,sum,n;
bool check(double t){
    Ll sum=0;
    for(Ll i=1;i<=n;i++){
        double c=(sqrt(1+4*a[i]/t)-1)/2.;
        Ll v=ceil(c);
        sum+=v;
        if(sum>k)return 0;
    }return 1;
}
int main()
{
    scanf("%lld%lld",&n,&k);
    for(Ll i=1;i<=n;i++)scanf("%lld",&x),a[i]=x;
    l=1e-9,r=12;//这里的精度我被卡成傻逼 
    while(r-l>1e-9){
        mid=(l+r)/2.;
        if(check(mid))r=mid;else l=mid;
    }
    for(Ll i=1;i<=n;i++){
        double c=(sqrt(1+4*a[i]/r)-1)/2.;
        Ll v=ceil(c);
        sum+=v;
        ans+=a[i]/v;
    }
    printf("%.0lf",ans-(k-sum)*r);
}

然后这道题目解决了我心中的一个疑惑,这个疑惑从我开始学信息学就伴随着我;
其实从这个问题上既可以看出我自己的能力有多弱了;
关于二分答案,我们最后需要去一个值;
我看到过很多代码,有点人取了r有的人取了l,还有的人取了mid
而且有点题目你只能特定的去一个值
这就让我很疑惑,所以我的代码总会在二分的时候维护一个ans
当check成功之后更新ans;
现在我终于知道什么时候取l,什么时候取r了;
在浮点二分的时候
当你二分的次数越多的时候,mid越加接近ans;
那么这个时候,如果我们要的值大于等于ans可以的话,我们用r,反之用l

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值