若存在一个合法的攀爬序列, 则其形式一定是 ,
于是考虑枚举 , 设 , 按 从大到小 排序, 然后 解出 爬出井 的最早时间, 更新答案, 总时间复杂度 .
考虑怎么优化, 记 , 设最早被淹死的时间为 , ,
只需要在 中 二分查找 即可得到 关于 的答案,
但是随着 的变化, 同样也在变化, 我们要使得这种变化更加 “平滑”, 更加易于控制一点,
于是我们将 从右往左 枚举, 考虑取出 会对哪些 造成影响,
会对 造成影响, 这种变化会使得 ,
由于 从右向左 枚举, 且 是 递减的, 所以 是不断增大的, 进而 是不断减少的,
所以 只有可能不断向左移动, 而向左移动最多移动 步, 所以按上述方法暴力移动 , 总复杂度是 的 .
但是这里要提到的是, 当出现 的情况时, 并非单调,
为了应对这种情况, 需要找出一个中准点 , 使得 是单调递增的, 在 中 二分查找 即可 .
- 注意一步就能跳到井口的情况需要特判 .
#include<bits/stdc++.h>
#define reg register
typedef long long ll;
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 100005;
int N;
int L;
ll C[maxn];
ll sum[maxn];
struct Pill{ int a, b, d; } A[maxn];
bool cmp(Pill x, Pill y){ return x.d > y.d; }
int lower_bound(int l, int r, const int &aim, const int &k){
int res = -1;
while(l <= r){
int mid = l+r >> 1;
ll tmp = sum[mid] + ((mid>=k)?(-A[k].d+A[mid+1].d):0);
(tmp >= aim) ? r = mid - 1, res = mid : l = mid + 1;
}
return res;
}
int main(){
N = read(), L = read();
for(reg int i = 1; i <= N; i ++) A[i].a = read(), A[i].b = read(), A[i].d = A[i].a - A[i].b;
std::sort(A+1, A+N+1, cmp);
for(reg int i = 1; i <= N; i ++) C[i] = C[i-1] + read();
int day = N;
for(reg int i = 1; i <= N; i ++){
sum[i] = sum[i-1] + A[i].d;
if(sum[i] <= C[i]) day = std::min(day, i);
if(sum[i] < sum[i-1] && (i-1)) day = std::min(day, i);
}
int Ans = -1;
for(reg int k = N; k >= 1; k --){
if(day >= k){
ll d = sum[day] - A[k].d + A[day+1].d;
while(day >= k && d <= C[day]) day --, d = sum[day]-A[k].d+A[day+1].d;
}
int pos = lower_bound(1, day, L-A[k].a, k);
if(pos != -1){ if(Ans == -1) Ans = pos; else Ans = std::min(Ans, pos); }
}
if(Ans != -1) Ans ++;
if(Ans == 2) Ans --;
printf("%d
", Ans);
return 0;
}