AT5760 Manga Market
首先一个想法就是可以考虑令 \(dp_{i, j}\) 表示当前考虑到了第 \(i\) 个商店,当前到了时刻 \(j\) 能走过最多的商店数量。但是你会发现这个 \(dp\) 转移的顺序并不是简单的从左往右转移,因为可能先走后面一个商店再走前面的一个商店,这时候我们一般的处理方法就是找到一种拓扑序使得这个 \(dp\) 能够转移。
我们会发现上面的 \(dp\) 的问题在于,可能会先走后面的商店再走前面的商店,那么我们能否对这些商店按某种方式排序后使得先走前面的商店一定比先走后面的商店更优呢?我们来试试看,对于任意两个商店 \(x, y\) 在某一时刻 \(t\) 如果先走 \(x\) 比先走 \(y\) 更优,则有:
暴力拆解之后可以得到:
因此我们只需要先将这些商店按照上面这个方法排序,这样就能保证转移顺序一定是从左往右了。再回到这个 \(dp\),你会发现这个 \(dp\) 的状态涉及 \(T\) 是不行的,能不能改写这个状态呢?你会发现直接将第二维和 \(dp\) 值直接调换即可,即令 \(dp_{i, j}\) 表示考虑完前 \(i\) 个商店,当前已经过 \(j\) 个商店的最小耗时。但是时间复杂度还是不够,但可以注意到的是如果 \(a_x \ne 0\) 则 \((t + 1)(a_x + 1) + b_x > 2t\) 也就是说我们最多会选 \(\log T\) 个 \(a_x > 0\) 的商店,这样我们 \(dp\) 的状态数就可以做到 \(O(n \log T)\) 了,转移的时候直接枚举上一次走的商店即可。注意到这是一个前缀 \(\max\) 的形式,\(dp\) 时一路维护前缀 \(\max\) 即可。
那么对于 \(a_x = 0\) 的商店呢?你会发现,这些 \(a_x = 0\) 的商店 \(x\) 放在最后走一定是最优的,因为如果放在前面走必然会因为后面存在一个 \(a_y > 0\) 使得 \(x\) 对时间的贡献会翻倍;而且我们一定会选择走 \(b_x\) 最小的那几个商店,所以我们先将 \(a_x = 0\) 的这部分商店按找 \(b_x\) 从小到大排序。因此,我们在最后统计答案的时候,对于每个 \(dp_{i, j}\) 找到最后一个 \(P\) 使得 \(\sum\limits_{k = 1} ^ P b_k + 1 \le T - dp_{i, j}\),直接记录 \(b\) 数组的前缀和二分即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for(int i = l; i <= r; ++i)
const int N = 200000 + 5;
const int M = 30 + 5;
const int inf = 1000000000 + 1;
struct node{
int a, b;
}a[N], b[N];
int n, m, T, cnt, ans, S[N], mx[M], dp[N][M];
int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
bool cmp1(node x, node y){
return 1ll * x.b * y.a - 1ll * x.a * y.b < x.a - y.a;
}
bool cmp2(node x, node y){
return x.b < y.b;
}
int main(){
cnt = read(), T = read();
rep(i, 1, cnt){
a[i].a = read(), a[i].b = read();
if(!a[i].a) b[++m] = a[i];
else a[++n] = a[i];
}
sort(a + 1, a + n + 1, cmp1), sort(b + 1, b + m + 1, cmp2);
rep(i, 0, n) rep(j, 0, M - 5) dp[i][j] = mx[j] = inf;
dp[0][0] = mx[0] = 0;
rep(i, 1, n){
rep(j, 1, M - 5) if(1ll * (mx[j - 1] + 1) * (a[i].a + 1) + a[i].b <= T){
dp[i][j] = (mx[j - 1] + 1) * (a[i].a + 1) + a[i].b;
}
rep(j, 1, M - 5) mx[j] = min(mx[j], dp[i][j]);
}
rep(i, 1, m) S[i] = min(S[i - 1] + b[i].b + 1, inf);
ans = upper_bound(S + 1, S + m + 1, T) - S - 1;
rep(i, 1, n) rep(j, 1, M - 5) if(dp[i][j] < inf){
int P = upper_bound(S + 1, S + m + 1, T - dp[i][j]) - S;
ans = max(ans, j + P - 1);
}
printf("%d", ans);
return 0;
}