题意
- 有m种商品,可以购买,每种商品只能用美元或英镑二者其一购买
- 你现在有s元,既不是美元,也不是英镑,需要兑换才能购买
- 你需要买k种商品
- 你可以在n天里进行购买,每天的汇率不同,汇率告诉你
- 问你花费最少多少天,可以完成购买任务。
思路
- 二分答案,对于第x天,计算它最少的花费f(x),<=s就是可行的,这是一个单调的函数,所以可以二分。
- 对于f(x)的计算,我用了nlog(n)的算法,遍历m个商品,用c[i]乘以前x天里,第t[i]种货币的最便宜的价格,存放到一个数组里,之后排序,计算前k个的和就是f(x)
- 前x天里,第t[i]种货币的最便宜的价格是通过预处理得到的,很容易的计算一下前缀最小值就行了。
实现
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pii;
#define fi first
#define se second
#define mp make_pair
const int maxn = 200005;
int a[maxn],b[maxn],t[maxn],c[maxn];
int n,m,k,s;
int am[maxn],bm[maxn];
int ida[maxn],idb[maxn];
const int inf = 0x3f3f3f3f;
pii pli[maxn];
int id[maxn];
ll f(int x){
ll ret = 0;
for (int i=1;i<=m;i++){
if (t[i] == 1){
pli[i].fi = (ll)c[i] * (ll)am[x];
}
else{
pli[i].fi = (ll)c[i] * (ll)bm[x];
}
pli[i].se = i;
}
sort(pli+1,pli+m+1);
for (int i=1;i<=k;i++){
ret += pli[i].fi;
}
return ret;
}
int main(){
cin>>n>>m>>k>>s;
am[0] = bm[0] = inf;
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
if (a[i] < am[i-1]){
am[i] = a[i];
ida[i] = i;
}
else{
am[i] = am[i-1];
ida[i] = ida[i-1];
}
}
for (int i=1;i<=n;i++){
scanf("%d",&b[i]);
if (b[i] < bm[i-1]){
bm[i] = b[i];
idb[i] = i;
}
else{
bm[i] = bm[i-1];
idb[i] = idb[i-1];
}
}
for (int i=1;i<=m;i++){
scanf("%d%d",&t[i],&c[i]);
}
ll low,high,mid;
low = 1,high = n;
ll d = -1;
while(low <= high){
mid = (low + high) / (ll)2;
if (f(mid) <= s){
high = mid - 1;
d = mid;
for (int i=1;i<=k;i++){
id[i] = pli[i].se;
}
}
else{
low = mid + 1;
}
}
cout << d <<"\n";
if (d == (ll)-1)
return 0;
int x = (int)d;
for (int i=1;i<=k;i++){
printf("%d %d\n",id[i],t[id[i]]==1?ida[x]:idb[x]);
}
return 0;
}