有 n n n 个商店,你在家里,初始时间为零。你从家走到一个商店或从一个商店走到另一个商店都需要一个单位时间。如果你在 t t t 个单位时间到达第 i i i 家商店,那么你排队 a i × t + b i a_i \times t + b_i ai×t+bi 个单位时间买东西,一家商店只能买一次。所有的商店会在 T + 0.5 T + 0.5 T+0.5 个单位时间关门,这时如果你在排队那么不算你买了东西。求你最多买多少次。
1 ≤ n ≤ 2 × 1 0 5 , 0 ≤ a i , b i , T ≤ 1 0 9 1 \leq n \leq 2 \times 10^5, 0 \leq a_i, b_i, T \leq 10^9 1≤n≤2×105,0≤ai,bi,T≤109。
可能直觉上排一个序完事了,但题目难度告诉我们这是错的,因为交换两个商店的顺序对其它的商店也会影响。
贪心不行,考虑可以 dp。令 f i , j f_{i,j} fi,j 走到的第 i i i 个商店为 j j j 的最小时间。不能走到一个商店两次,看上去没法转移。所以还是要排序。考虑在 t t t 的时间要出发,接下来要访问两个商店 i i i 和 j j j。如果访问第一个商店比第二个商店更优,那么
1 + a i ( t + 1 ) + b i + 1 + a j ( t + 1 + a i ( t + 1 ) + b i + 1 ) + b j < 1 + a j ( t + 1 ) + b j + 1 + a i ( t + 1 + a j ( t + 1 ) + b j + 1 ) + b i ⇕ a j ( b i + 1 ) < a i ( b j + 1 ) 1 + a_i(t+1) + b_i + 1 + a_j (t + 1 + a_i(t+1) + b_i + 1) + b_j < 1 + a_j(t+1) + b_j + 1 + a_i (t + 1 + a_j(t+1) + b_j + 1) + b_i \\ \Updownarrow \\ a_j(b_i+1) < a_i(b_j+1) 1+ai(t+1)+bi+1+aj(t+1+ai(t+1)+bi+1)+bj<1+aj(t+1)+bj+1+ai(t+1+aj(t+1)+bj+1)+bi⇕aj(bi+1)<ai(bj+1)
那么可以转移了,而且状态中的 j j j 没必要可以不写。那么这是一个 O ( n 2 ) O(n^2) O(n2) 的超时 dp。可以发现 a i × t + b i a_i \times t + b_i ai×t+bi 即使 a i = 1 a_i = 1 ai=1 也是成倍增长的,这意味着如果 a i > 0 a_i > 0 ai>0 那么最多经过 l o g ( T ) log(T) log(T) 个商店,所以 30 30 30 个商店完全够了。
所以将 a i = 0 a_i = 0 ai=0 的挑出来,如果答案中经过它们,那么在最后走一定最优。可以将它们按 b i b_i bi 从小到大排序。枚举去多少个 a i > 0 a_i > 0 ai>0 的商店,二分找到可以去的 a i = 0 a_i = 0 ai=0 的商店,两部分加起来更新答案即可。
时间复杂度 O ( n log n + n log T ) O(n \log n + n \log T) O(nlogn+nlogT)。不要用 memset 因为你的正无穷乘上一个数可能爆 long long。
#include
using namespace std;
#define pb push_back
#define int long long
#define F first
#define S second
typedef pair<int, int> P;
const int N = 30 + 5, INF = 0x3f3f3f3f;
inline int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
bool cmp(P a, P b) {
return b.F * (a.S + 1) < a.F *(b.S + 1) ;
}
vector <P> a; vector <int> b;
int f[N];
signed main() {
int n = read(), T = read();
for (int i = 1; i <= n; i++) {
int x = read(), y = read();
if (x) a.pb(make_pair(x, y));
else b.pb(y + 1);
}
sort(a.begin(), a.end(), cmp);
sort(b.begin(), b.end());
for (int i = 1; i < b.size(); i++) b[i] += b[i - 1];
for (int i = 1; i <= 30; i++) f[i] = T + 1;
for (int i = 0; i < a.size(); i++)
for (int j = 30; j; j--)
f[j] = min(f[j], (f[j - 1] + 1) * (a[i].F + 1) + a[i].S);
int ans = 0;
for (int i = 0; i <= 30; i++)
if (f[i] <= T) ans = max(ans, i + (b.empty() ? 0 : upper_bound(b.begin(), b.end(), T - f[i]) - b.begin()));
printf("%lld\n", ans);
return 0;
}