Codeforces 1165F1&&F2 Round #560 (Div. 3) - Microtransactions【二分法】

F2. Microtransactions (hard version)
time limit per test3 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
The only difference between easy and hard versions is constraints.

Ivan plays a computer game that contains some microtransactions to make characters look cooler. Since Ivan wants his character to be really cool, he wants to use some of these microtransactions — and he won’t start playing until he gets all of them.

Each day (during the morning) Ivan earns exactly one burle.

There are nn types of microtransactions in the game. Each microtransaction costs 2 burles usually and 1 burle if it is on sale. Ivan has to order exactly kiki microtransactions of the ii-th type (he orders microtransactions during the evening).

Ivan can order any (possibly zero) number of microtransactions of any types during any day (of course, if he has enough money to do it). If the microtransaction he wants to order is on sale then he can buy it for 1 burle and otherwise he can buy it for 2 burles.

There are also mm special offers in the game shop. The j-th offer (dj,tj) means that microtransactions of the tj-th type are on sale during the dj-th day.

Ivan wants to order all microtransactions as soon as possible. Your task is to calculate the minimum day when he can buy all microtransactions he want and actually start playing.

Input
The first line of the input contains two integers n and m (1≤n,m≤2⋅105) — the number of types of microtransactions and the number of special offers in the game shop.

The second line of the input contains n integers k1,k2,…,kn (0≤ki≤2⋅105), where ki is the number of copies of microtransaction of the i-th type Ivan has to order. It is guaranteed that sum of all ki is not less than 1 and not greater than 2⋅105。

The next mm lines contain special offers. The j-th of these lines contains the j-th special offer. It is given as a pair of integers (dj,tj)(dj,tj) (1≤dj≤2⋅105,1≤tj≤n) and means that microtransactions of the tj-th type are on sale during the dj-th day.

Output
Print one integer — the minimum day when Ivan can order all microtransactions he wants and actually start playing.

Examples
input
5 6
1 2 0 2 0
2 4
3 3
1 5
1 2
1 5
2 3
output
8
input
5 3
4 2 1 3 2
3 5
4 2
2 5
output
20

题意:就是有个人,每天早上能赚1burle(就当是1块钱吧),然后他晚上进行微型交易(不知道是不是这样翻译的???就假设这个交易就是买汽水好了),每瓶汽水平时都是2块钱,打折的时候是1块钱。现在他要买n种汽水,每种汽水都要ki瓶(1<= i <=n)。现在有一个商店有m次打折的时候,第d天打折的商品是第t号汽水,问你要多少天才能完成你买汽水的要求。

思路:由题知要多少天就是要多少钱,就是每瓶汽水的价格都是2块钱的,情况1:假设刚好它全部汽水都是打折时候买的,所以天数就是所有汽水加起来的数量(Sum)。情况2:假设刚好全部汽水都没打折(都是两块钱买的),所以天数就是所有汽水加起来的数量(Sum) x 2。那么天数Day的范围就是(Sum<=Day<=Sum x 2)。在这个范围用二分法可以加快速度。

分析:汽水应该怎样买才能选出最优解呢?假设n天能买完所有汽水,那就选第n天之前最近的一次打折的汽水,买完这种汽水所要的数量。为什么呢?假设这种汽水在两个不同的天数都有打折,如果你在较前面的天数就买了这种汽水,那么如果这两天之间,有另一种汽水也打折(并且要求数量还很多),如果你前面就买了这种汽水就可能会导致你后面买另外一种汽水的时候无法购买足够多的数量,从而导致你要花2块钱去买完这种汽水…(解释到这里就应该懂了吧)

代码贴上:

#include<algorithm>
#include<vector>
#include<cmath>
#include<iostream>

using namespace std;
int k[200005];
vector<int> a[400005];//用vector记录每天的打折情况
int kk[400005];
int n, m, sum;

bool CheckDay(int mid)
{
	int num = sum;
	for (int i = 1; i <= n; ++i)
	{
		kk[i] = k[i];
	}
	int left = mid;//剩下的钱
	int x = 0;
	for (int i = mid; i >= 1 && left; --i)
	{
		for (int j = 0; j < a[i].size(); ++j)
		{
			if (!kk[a[i][j]])continue;
			int buy = min(kk[a[i][j]], left);//能买商品的数量=min(要买商品的数量 和 剩下钱)
			kk[a[i][j]] -= buy;
			num -= buy;
			left -= buy;
		}
		if (left == i) //剩下的钱大于天数
		{
			x++;//剩下的钱数
			left = i - 1;
		}
		/*
		为什么剩下的钱数要用上面那种方法表示而不直接用left呢?
		因为我们是从第mid天倒推回来的
		如果第mid天没有买到打折商品,那么往前推天数减1 赚到的钱就少1
		倒推能保证第mid买商品的时候有足够的钱.
		*/
	}
	return x >= num * 2;//应该是剩下的钱大于要买的碟子*2
}

int main()
{

	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
	{
		cin >> k[i];
		sum += k[i];
	}
	int d, t;
	for (int i = 0; i < m; ++i)
	{
		cin >> d >> t;
		a[d].push_back(t);
	}
	int l = sum, r = sum * 2;
	int Day;
	while (l <= r)//二分法求解
	{
		int mid = (l + r) / 2;
		if (CheckDay(mid))
		{
			Day = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	cout << Day << endl;
	return 0;
}

无了呀嘻嘻

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值