这是第一道接近做出来的二分,唯一差的就是没有考虑数据范围(因为我这个菜鸡能做的题很少会让我卡long long)。
这个题的分组存在一个很明显的临界值,所以开始考虑二分。然后开始考虑check怎么想(要不是队友解释了题目,我估计看样例就要long long)。
我当时考虑的是,全取左端点,然后把多余的部分保留下来,进行判断。(考虑右端点不会爆long long)
因为每一个问题库还有一个范围,所以 问题库/分组 的值最大也不能超过他自身的右端点r,多余的部分就可以用
sum += min((a[i].ri -a[i].li )*m , a[i].s -a[i].li*m);
这样就可以写出最关键的判断条件,minn是所有左端点的总和,sum是所有多出来的部分。
if(minn+sum/m>=l&&minn<=r){
return true;
}
我这里写了minn<=r,也就是问题库范围左端点总和是小于要求的右端点的,那么就可以不用进行特判了(没那个脑子想特判了)
我做这个题有三个点需要注意:
一个是数据在1e9的,优先考虑从小的方面开始找,不然很有可能就爆了longlnog。
第二个是二分算法,把右边界开到1e18,我最开始写了1e9,简直在找死。
第三个是爆了long long,直接尝试一发int 128 ,long double 也可以把问题避免掉 。
最后考虑了一下为什么要写long double,不一定对:
因为考虑数据极端情况,每一个组都是0 1 ,一共可以有1e9组问题,那么问题集就能被开到1e18,那么sum最大值就是1e27。
总之,像如这种写了1e18,还有乘一个什么东西的,还是开大点吧。
能研究出这么做对的,还是靠一个队交了12发,不然我都以为这个题我做法错了,估计他们范围也改的很崩溃吧hhh.
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
typedef long long ll;
struct node{
ll s,li,ri,ti;
};
node a[N];
ll n,l,r;
bool check(long double m){
if(m==0) return true;
long double sum = 0;
long double minn = 0;
for(int i = 1;i<=n;i++){
minn += a[i].li;
if(a[i].li !=0){ //这地方写乘就不用特判了
if(a[i].s/a[i].li <m){
return false;
}
}
sum +=min( (a[i].ri -a[i].li )*m , a[i].s -a[i].li*m );
}
//cout<<m<<" "<<sum<<endl;
if(minn+sum/m>=l&&minn<=r){
return true;
}
else{
return false;
}
}
int main(){
scanf("%lld%lld%lld",&n,&l,&r);
for(int i = 1;i<=n;i++){
scanf("%lld",&a[i].s );
}
for(int i = 1;i<=n;i++){
scanf("%lld%lld",&a[i].li ,&a[i].ri );
}
ll s = 0,t = 1e18;
while(s<=t){
long double m = (s+t)/2;
if(check(m)==true){
s = m+1;
}
else{
t = m-1;
}
// cout<<s<<" "<<t<<endl;
}
printf("%lld",t);
}