1. 程式人生 > >2018 雅禮國慶集訓

2018 雅禮國慶集訓

Merchant

    有n個物品,第i個物品有兩個屬性ki , bi,表示它在時刻x的價值為ki × x + bi . 當前處於時刻0,你可以選擇不超過m個物品,使得存在某個整數時刻t, t ≥ 0,你選擇的 所有物品的總價值大於等於S. 給出S,求t的最小值。

很容易想到幾個物品加起來的時候,價值是一個一次函式(合併同類項)

   然後畫一下圖可以發現(然而我考試時並沒有發現),在同一個x座標下對應一個最大值,這個最大值是先減後增的。

   所以我們可以先判斷一下0時刻,如果可以就輸出0,不然就二分

  每一次判斷的時候算出此時的價值,然後用一個騷操作nth_element, 可以求出前m個最大的,複雜度O(n)

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 1e6 + 10;
int k[MAXN], b[MAXN], n, m;
ll c[MAXN], s;

bool check(int t)
{
	ll sum = 0;
	REP(i, 0, n) c[i] = 1ll * k[i] * t + b[i];
	nth_element(c, c + m, c + n, greater<ll>()); //從大到小 
	REP(i, 0, m) 
		if((sum += c[i]) >= s) //注意可能後面的值為負,所以不一定全部加完才最大 
		 return true;
	return false;
}

int main()
{
	scanf("%d%d%lld", &n, &m, &s);
	REP(i, 0, n) scanf("%d%d", &k[i], &b[i]);
	if(check(0)) { puts("0"); return 0; }

	int l = 0, r = 1e9 + 1;
	while(l + 1 < r)
	{
		int m = (l + r) >> 1;
		if(check(m)) r = m;
		else l = m;
	}
	printf("%d\n", r);

	return 0;
}