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

来源:互联网 发布:层次方框图软件 编辑:程序博客网 时间:2024/05/17 04:08

感觉和吃了屎一样,很难受;
做了做了一天就做了一道题目,现在才调出来,明天要考化学,感觉自己跟个傻逼一样;
我知道我自己傻逼的原因,我真的受够了;
另外,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 longusing 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

原创粉丝点击