ABC351E

一类套路题

有两种购买物品的方式,花p元购买a个物品或者花q元购买b个物品。求至少买w个物品的情况下,花钱的最少数。

这种套路题用数学表示为,在满足 的情况下,最小化 的值。

解法

怎么做呢?首先你可能会想到用斜率+贪心的方法,但这肯定是错的。

事实上有这么一个结论:

不可能同时用花p元的方式购买b次且用花q元的方式购买a次。

如何证明呢?首先我们考虑购买ab个物品,如果用p元的方法会花pb元,而用另一种方法会花qa元。

如果我们同时购买很多次ab个物品,那么一定会只使用花费为pb或者qa中的一种的方法,看二者哪个更小。

因此的话,有种 的方法:从 枚举花p元的方式,再从 枚举花q元的方式。

ABC374E

在上面套路添加了一层二分,事实上二分很好想,但上面的贪心套路才是关键。

这里会爆 int ,因此我为了方便就 #define int long long 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<bits/stdc++.h>
typedef long long ll;
#define int long long
constexpr int _{110};

int a[_], b[_], p[_], q[_];
int n, x;
signed main()
{
std::cin >> n >> x;
for(int i = 0; i < n; ++i)
std::cin >> a[i] >> p[i] >> b[i] >> q[i];
int l{1}, r{1000000000};
int ans{};
while(l <= r)
{
int mid{l+r>>1};
ll sum{};
for(int i = 0; i < n; ++i)
{
int smn{x+1};
for(int j = 0; j < b[i]; ++j)
{
int s{};
if(j * a[i] >= mid)
{
s = j * p[i];
}
else
{
s = j * p[i] + (mid - j * a[i] + b[i] - 1) / b[i] * q[i];
}
smn = std::min(smn, s);
}
for(int j = 0; j < a[i]; ++j)
{
int s{};
if(j * b[i] >= mid)
{
s = j * q[i];
}
else
{
s = j * q[i] + (mid - j * b[i] + a[i] - 1) / a[i] * p[i];
}
smn = std::min(smn, s);
}

sum += smn;
}

if(sum <= x)
{
ans = mid;
l = mid + 1;
}
else
{
r = mid - 1;
}
}
std::cout << ans;
return 0;
}