https://ac.nowcoder.com/acm/contest/22353/1011
并不需要高级数据结构,对答案二分即可。
假定当前二分的答案为
x
x
x,则
∑
v
i
∑
w
i
≥
x
\frac{ \sum_{v_i} }{\sum_{w_i}} ≥ x
∑wi∑vi≥x 成立时
x
x
x 才可能是最后的答案。
化简式子可以得到
∑
\sum
∑
(
v
i
−
x
∗
w
i
)
≥
0
(v_i-x*w_i) ≥ 0
(vi−x∗wi)≥0。
由此可以写出
c
h
e
c
k
check
check 函数,不断逼近答案即可。
注意本题为实数域上的二分,用
m
i
d
+
1
mid+1
mid+1 会导致精度丧失。
#include<bits/stdc++.h>
#define endl '\n'
#define pii pair<int,int>
using namespace std;
using ll = long long;
const int maxn = 5e5+3;
struct item
{
double w,v,tmp; // 体积、价值
}a[maxn];
int n,k;
double ans;
bool cmp(item x,item y)
{
return x.tmp>y.tmp;
}
bool check(double x) // 比例到x能不能达到
{
for(int i=1;i<=n;i++)
a[i].tmp=a[i].v-x*a[i].w;
// 只对前 k 个元素排好序,复杂度为线性
nth_element(a+1,a+1+k,a+1+n,cmp);
double sum=0;
double w_sum=0,v_sum=0;
for(int i=1;i<=k;i++)
{
sum+=a[i].tmp;
w_sum+=a[i].w;
v_sum+=a[i].v;
}
if(sum>0)
{
ans=(v_sum*1.0)/(w_sum*1.0);
return true;
}
else return false;
}
double eps=1e-12;
void solve()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i].w>>a[i].v;
}
double l=0,r=1e18;
while(r-l>max(1.0,l)*eps)
{
double mid=(l+r)/2.0;
if(check(mid))
{
l=mid;
}
else r=mid;
}
cout<<fixed<<setprecision(10)<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--)
solve();
return 0;
}