雅礼集训 Day6 T1 Merchant 解题报告

Merchant

题目描述

(n)个物品,第(i)个物品有两个属性(k_i,b_i),表示它在时刻(x)的价值为(k_i imes x+b_i).

当前处于时刻(0),你可以选择不超过(m)个物品,使得存在某个整数时刻(t,t≥ 0),你选择的所有物品的总价值大于等于(S).

给出(S),求(t)的最小值。

输入输出格式

输入格式

第一行三个整数(n,m,S).

接下来(n)行,第(i)行两个整数(k_i,b_i).

输出格式

一行一个整数表示答案.

数据范围

对于所有数据,有(1le mle nle 10^6,-10^9 le b_i le 10^9,-10^6 le k_i le 10^6,0 le S le 10^{18}).

数据保证有解,且答案不超过(10^9)

  • ( ext{Subtask}1(22\%)), (n ≤ 20).
  • ( ext{Subtask}2(36\%)), (n ≤ 10^5),(0 ≤ k_i ≤ 10^4).
  • ( ext{Subtask}3(8\%)), (k_i ≤ 0).
  • ( ext{Subtask}4(12\%)), (n ≤ 10^5).
  • ( ext{Subtask}5(22\%)), 没有特殊的约束。

一开始大家都想二分(t)

但很快发现这样是不对哒

可事实上又是可以哒

(t)的造成的最大的可取集合在值域上要么单调增,要么先单减后单增。

对于后者,我们先判(0),然后二分就行了

发现这样如果sort是(nlognlog1e9)

但找第k大可以(O(n))

实现是快排只进入一边

可以用(nth\_element)


Code:

#include <cstdio>
#include <algorithm>
#define ll long long
const int N=1e6+10;
int n,m;
ll k[N],b[N],d[N],S;
ll read()
{
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-f;c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
    return x*f;
}
bool check(ll t)
{
    for(int i=1;i<=n;i++)
        d[i]=k[i]*t+b[i];
    std::nth_element(d+1,d+n-m,d+1+n);
    ll sum=0;
    for(int i=n;i>n-m;i--)
    {
        sum+=d[i]>0?d[i]:0;
        if(sum>=S) return true;
    }
    return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    S=read();
    for(int i=1;i<=n;i++)
        k[i]=read(),b[i]=read();
    ll l=0,r=1e9;
    if(check(l)) return puts("0"),0;
    while(l<r)
    {
        ll mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    printf("%lld
",l);
    return 0;
}


2018.10.6

原文地址:https://www.cnblogs.com/butterflydew/p/9748089.html