Codeforces Round #657 (Div. 2)——C. Choosing flowers题解

2020/7/20
讲道理,这场Div2挺搞的。。。不过侥幸偷了50多分。嘻嘻,说正事:
题目:
Vladimir would like to prepare a present for his wife: they have an anniversary! He decided to buy her exactly n flowers.
Vladimir went to a flower shop, and he was amazed to see that there are m types of flowers being sold there, and there is unlimited supply of flowers of each type. Vladimir wants to choose flowers to maximize the happiness of his wife. He knows that after receiving the first flower of the i-th type happiness of his wife increases by ai and after receiving each consecutive flower of this type her happiness increases by bi. That is, if among the chosen flowers there are xi>0 flowers of type i, his wife gets ai+(xi−1)⋅bi additional happiness (and if there are no flowers of type i, she gets nothing for this particular type).
Please help Vladimir to choose exactly n flowers to maximize the total happiness of his wife.
大致题意:
有m种花,每种花有第一次购买获得的数值与第k次购买获得的数值(k>1),问你买n朵花获得的最大数值是多少。
总结:
明显感觉不算一道难题(虽然我也不会),做出来的人却没多人,可能是AB太毒瘤了。尝试过优先队列贪心(时间复杂度过大)和直接排序贪心(WA),最后看题解才知道,算是一道前缀和+二分题吧。
思路:
显而易见,购买k次以上(k>1)的花只会买一种(当然也有可能0种,后面再考虑一下即可)。那么只需要找到是哪种花购买1次以上,对于购买了k次的花记为x,则我们会发现如果少购买一次bx,去购买一个ai(ai>bx)则会使得总数值更高,因此对于每种bx,我们在购买它之前需要把所有ai>bx的花都先买下来。因此可以先按照ai进行排序,然后记录下ai的前缀和sum[i],之后枚举每一个bi,二分查找到最大的p使得ap>bi(即为最小的ap使得ap>bi),剩下的花就全部购买bi。
处理过程需要注意:

  1. 如果p>=n,则最多只能购买sum[n]而不是sum[p]
  2. 如果i>p,则说明当前bi对应的第一次购买ai还没有被购买,需要先购买一次ai,剩下的n-p-1次才能购买bi。
  3. 如果i<=p,剩下的n-p次全部购买bi。

代码:
(码风很飘,轻喷,可以去cf上面看看大佬的代码,或者自己写,大致思路如上)

#include
using namespace std;
#define pll pair
pair<long long, long long>x[100005];
long long sum[100005];
int upper(int l,int r,long long num)
{
	int ans = -1;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (x[mid].first > num)
		{
			ans = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	return ans;
}
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, m;
		cin >> n >> m;
		int i;
		for (i = 0; i < m; i++)
		{
			cin >> x[i].first >> x[i].second;
			sum[i] = 0;
		}
		sort(x, x + m, greater<pair<long long, long long>>());
		for (i = 0; i < m; i++)
		{
			//cout << x[i].first << ' ' << x[i].second << endl;
			if (i)sum[i] = x[i].first + sum[i - 1];
			else sum[i] = x[i].first;
		}
		long long ans = 0;
		for (i = 0; i < m; i++)
		{
			int p = upper(0, m-1, x[i].second);
			if (p != -1)
			{
				ans = max(ans, sum[min(p, n-1)] + (p < i ? max(0, n - p - 2) * x[i].second + (n>p+1?x[i].first:0) : max(0, n - p-1) * x[i].second));
			}
			else
			{
				ans = max(ans,  (n-1) * x[i].second + x[i].first);
			}
		}
		long long ans2 = 0;
		for (i = 0; i < m && n; i++)
		{
			ans2 += x[i].first;
			n--;
		}
		cout << max(ans2, ans) << endl;
	}
	return 0;
}

你可能感兴趣的:(刷题记录,算法,c++)