【题解】CF1165F2 Microtransactions (hard version)
CF1165F2 Microtransactions (hard version)
题目描述
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 n n n types of microtransactions in the game. Each microtransaction costs 2 2 2 burles usually and 1 1 1 burle if it is on sale. Ivan has to order exactly ki k_i ki microtransactions of the i i i -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 1 1 burle and otherwise he can buy it for 2 2 2 burles.
There are also m m m special offers in the game shop. The j j j -th offer (dj,tj) (d_j, t_j) (dj,tj) means that microtransactions of the tj t_j tj -th type are on sale during the dj d_j 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.
输入格式
The first line of the input contains two integers n n n and m m m ( 1≤n,m≤2⋅105 1 \le n, m \le 2 \cdot 10^5 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 n n integers k1,k2,…,kn k_1, k_2, \dots, k_n k1,k2,…,kn ( 0≤ki≤2⋅105 0 \le k_i \le 2 \cdot 10^5 0≤ki≤2⋅105 ), where ki k_i ki is the number of copies of microtransaction of the i i i -th type Ivan has to order. It is guaranteed that sum of all ki k_i ki is not less than 1 1 1 and not greater than 2⋅105 2 \cdot 10^5 2⋅105 .
The next m m m lines contain special offers. The j j j -th of these lines contains the j j j -th special offer. It is given as a pair of integers (dj,tj) (d_j, t_j) (dj,tj) ( 1≤dj≤2⋅105,1≤tj≤n 1 \le d_j \le 2 \cdot 10^5, 1 \le t_j \le n 1≤dj≤2⋅105,1≤tj≤n ) and means that microtransactions of the tj t_j tj -th type are on sale during the dj d_j dj -th day.
输出格式
Print one integer — the minimum day when Ivan can order all microtransactions he wants and actually start playing.
题意翻译
题目描述
有n种物品,对于第i个物品,你需要买ki个,每个物品在非打折日买是2块钱,在打折日买是1块钱,每天你可以赚1块钱,一共有m个打折日,在第di天第ti种物品打折,最少需要多少天可以买完你需要的物品
输入格式
第一行包含两个整数n和m(1≤n,m≤2e5)中的商品类型数和打折的天数。
输入的第二行包含n个整数ki,其中ki是第i类需要的个数。(0≤Σki≤2e5)。
接下来的m行,每行两个数di,ti)(1≤di≤2e5,1≤ti≤n)表示第ti个商品在di天打特价
输出格式
一个整数,表示最早在哪一天能买完所有需要的商品
输入输出样例
输入 #1
5 6
1 2 0 2 0
2 4
3 3
1 5
1 2
1 5
2 3
输出 #1
8
输入 #2
5 3
4 2 1 3 2
3 5
4 2
2 5
输出 #2
20
解
大体思路:二分最短天数,判断这些天数里能否买完所有物品
判断能否买完的函数check(int x):
主要的参数其实就一个x,表示天数。在这些天里,我们一共可以赚x元钱,设dollor=x表示总钱数。具体在买东西的时候,我们先把能打折的买了,最后在以固定单价2元清算无法打折的原价商品。在买东西的时候,我们把总钱数dollor随时减去所花的钱,最后如果dollor>=0,说明可行,return 1;否则 return 0。
最优地买打折的商品——贪心
- 参数:a[i]
a[i]表示第i种商品需要的数量
- 参数:vector
sale[i]
有的商品不打折,有的商品不止一天打折,每一天可能有好几种商品打折。我们用一个不定长数组sale[i]存第i中商品在哪些天打折,就像这样:
for(int i=1;i<=m;i++){
cin>>xx>>yy;//在第xx天,yy商品打折
sale[yy].push_back(xx);
}
有这么多天可以给我打折,我在哪一天买呢?是只要打折我就能买多少买多少,然后从需要的数量a[i]里减去已购买的,直到a[i]==0或是所有的打折日都过去了吗?
事实上,我们可以只在第i个商品的最后一个打折日买所有能买的。之前的打折日我不买东西,把钱留到最后一天来用,不会造成什么影响。但是如果我在第一个打折日就花了我的钱去买第i个东西,有可能会让本来可以打折购买的商品j没钱买,所以答案会更劣。也就是说,如果我们总是在第i个商品的最后一个打折日尽可能的买东西的话,会让结果最优。这就是在买打折商品时应用的贪心思想。
所以我们要给sale[i]排序,在天数x里找到小于等于x的最后一个打折日,来买商品i。
- 参数:pos[i]
是sale[i]的下表,sale[i] [pos[i]]表示最后一个打折日。
like this:
for(int i=1;i<=n;i++){//寻找1~n件商品的pos
pos[i]=sale[i].size()-1;//初定为最后一天,防止找不到那个合法的“最后一个打折日”的时候pos[i]随随便便等于个什么值,然后出事
for(int j=1;j<sale[i].size();j++){
if(sale[i][j] > x){//如果这一天比x来的晚
pos[i]=j-1;//那上一天就是我们要找的最后一个打折日
break;//找到了赶紧溜
}
}
}
现在我们已经确定了第i个物品在哪一天买,接下来就要从第一天往第x天来买东西了。
具体地,根据这个最后一个打折日sale[i] [pos[i]] 为关键字来确定商品顺序。
like this:
int tem[200010];
int main(){
for(int i=1;i<=n;i++){
tem[i]=i;
}
}
bool cmp(int x,int y){
return sale[x][pos[x]] < sale[y][pos[y]];
}
bool check(int x){
sort(tem+1,tem+1+n,cmp);
}
然后我们就可以愉快的买打折商品了~
- 参数:t1,money,left
在每一天,我们用一个money表示手中能自由支配的钱
t1=0;//表示我还没花钱
left=0;
for(int i=1;i<=n;i++){//枚举i,tem[i]表示商品
int money=sale[tem[i]][pos[tem[i]]]-t1;//sale[tem[i]][pos[tem[i]]]表示天数,t1是目前已经花的钱,money=sale[tem[i]][pos[tem[i]]]-t1表示我目前能花的钱
if(money >= a[tem[i]]){//我有money元,只要买a[tem[i]]件商品
dollor -= a[tem[i]];//那就都买了,总钱数dollor减去相应的花费
t1+=a[tem[i]];//钱,花了,没了
}
else{
dollor -= money;//money不够买所有的商品时,只好能买多少买多少
left+=a[tem[i]]-money;//left是无法以打折价格购买的,存下来以原价2元买。进了这个else的可能是真的在这一天打折但是钱不够买所有;也可能是根本没有打折日的混子,没有打折日pos[tem[i]]就等于0,在第0天只有0元钱啥也买不了
t1+=money;
}
}
dollor-=left*2;
注意不打折的商品我们人为令其sale[] [0] =0 , pos等于0, 这样它就会在第0天被买
就是说要往sale[i]里先塞一个0。like this:
int main(){
for(int i=1;i<=n;i++){
sale[i].push_back(0);
}
}
that's all
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<bitset>
#include<set>
#include<map>
using namespace std;
int n,m,a[200010],xx,yy,pos[200010],ans;
vector <int> sale[200010];
int tem[200010];
bool cmp(int x,int y){
return sale[x][pos[x]] < sale[y][pos[y]];
}
bool check(int x){
int dollor=x,left=0;
for(int i=1;i<=n;i++){
pos[i]=sale[i].size()-1;
for(int j=1;j<sale[i].size();j++){
if(sale[i][j] > x){
pos[i]=j-1;
break;
}
}
}
sort(tem+1,tem+1+n,cmp);
int t1=0;//表示我还没花钱
//按pos[i]从前往后for
for(int i=1;i<=n;i++){//枚举i,tem[i]表示商品
int money=sale[tem[i]][pos[tem[i]]]-t1;//sale[tem[i]][pos[tem[i]]]表示天数,t1是目前已经花的钱,money=sale[tem[i]][pos[tem[i]]]-t1表示我目前能花的钱
if(money >= a[tem[i]]){//我有money元,只要买a[tem[i]]件商品
dollor -= a[tem[i]];//那就都买了,总钱数dollor减去相应的花费
t1+=a[tem[i]];//钱,花了,没了
}
else{
dollor -= money;//money不够买所有的商品时,只好能买多少买多少
left+=a[tem[i]]-money;//left是无法以打折价格购买的,存下来以原价2元买。进了这个else的可能是真的在这一天打折但是钱不够买所有;也可能是根本没有打折日的混子,没有打折日pos[tem[i]]就等于0,在第0天只有0元钱啥也买不了
t1+=money;
}
}
dollor -= left*2;
return dollor>=0?1:0;
}
int main(){
cin >> n >> m;
for(int i=1;i<=n;i++){
tem[i]=i;sale[i].push_back(0);
}
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++){
cin>>xx>>yy;
sale[yy].push_back(xx);
}
for(int i=1;i<=n;i++){
sort(sale[i].begin(), sale[i].end() );//vector排序,前闭后闭
}
int l,r,mid;
l=0;r=400010;
while(l<r){
mid=(l+r)>>1;
if(check(mid)) r=mid;
else
l=mid+1;
}
printf("%d\n",l);
return 0;
}