最近复习了一下堆,于是去luogu上找一些简单题写一写

luogu P2107 小Z的AK计划-LMLPHP

贪心的想,小z不会到一半以后回头去Ak,因为这样从时间上想肯定是不优的,他可以早在之间经过时就AK
所以我们可以将所有机房按照横坐标排序
可以想到的是,我们最后肯定是要走过所有的机房,也就是说路程消耗的疲劳值是不可避免的。
我们只能尽可能的减少小ZAK所花费的时间
贪心的考虑,当我们在机房Ak所花费的时间长时,我们可能能在这个时间内AK更多的机房
所以当时间出问题时,我们肯定要取出堆顶删除以便AK更多的机房。
我们维护一个关于机房AK时间的大根堆,每次先假定要Ak,然后将时间丢入堆中,所到当前机房所花费的时间比总时间大,则移除堆顶
但是需要注意的是,移除堆顶时,我们的答案并不需要减少,因为刚刚插入一个,然后超过了总时间,在移除一个,刚刚好抵消
至于为什么只需要移除堆顶:
我在题解上看到过用while循环去移除堆顶的,然而实际并不需要,因为我们刚刚插入一个新元素
对于这个元素来说,若他不是堆中最大元素,显然我们移除最大的肯定就把这个新元素占的地方给腾出来了
若是最大元素,那么直接删除就等于过而不入,对答案没有影响

这样我们就能AC这道题

嗯好吧,其实思路是有些问题的

因为是这样的,可能的是,我们在当前这个机房,即使过而不入,单单是走过去,在加上之前的选择并AK机房耗费的时间就把总时间给超了

这样我们不得不多次弹出堆顶,同时在弹出第一个堆顶后,剩下的弹出多少个,答案数就要减去多少

好在这题没有卡

不会且没看stl的我选择手写二叉堆

 #include<bits/stdc++.h>
#define ll long long
#define uint unsigned int
#define ull unsigned long long
using namespace std;
const int maxn = ;
struct shiki {
ll x, t;
}a[maxn];
ll heap[maxn << ], tot = , num = ;
ll n, m, top = ;
ll ans = ; inline ll read() {
ll x = , y = ;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') y = -;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << ) + (x << ) + ch - '';
ch = getchar();
}
return x * y;
} inline void up(int p) {
while(p > ) {
if(heap[p] > heap[p / ]) {
swap(heap[p], heap[p / ]);
p /= ;
}
else break;
}
} inline void down(int p) {
int s = p * ;
while(s <= tot) {
if(s < tot && heap[s] < heap[s + ]) s++;
if(heap[p] < heap[s]) {
swap(heap[s], heap[p]);
p = s, s = p >> ;
}
else break;
}
} inline void extract() {
heap[] = heap[tot--];
down();
} inline void insert(ll k) {
heap[++tot] = k;
up(tot);
} inline bool cmp(shiki a, shiki b) {
return a.x < b.x;} inline int get_top(){return heap[];} int main() {
n = read(), m = read();
for(int i = ; i <= n; ++i) {
ll x = read(), t = read();
if(x <= m && t <= m) {
a[++top].x = x;
a[top].t = t;
}
}
sort(a + , a + top + , cmp);
for(int i = ; i <= top; ++i) {
insert(a[i].t);
num += (a[i].x - a[i - ].x) + a[i].t;
if(num <= m) ans++;
if(num > m) {
num -= get_top();
extract();
}
}
printf("%lld\n", ans);
return ;
}
05-16 17:56