题目大意:(吐槽计算机组成原理)计算机内存分块构成,由 1 − n 1-n 1−n编号。现在要求求缓存的块数,使得对于给定的长度为 n n n的请求序列,少 k k k次命中缓存。其中,在命中缓存时有三种情况:
- 缓存中存在当前请求编号的块,此时命中该编号的缓存;
- 未命中缓存,缓存未满,当前块写入缓存;
- 未命中缓存,缓存已满,替换缓存中时间最久的块;
思路:二分一个长度并进行检验,检验时用一个 s e t set set维护 C a c h e Cache Cache中的块编号,并按照请求序列的先后顺序进行排序,用一个 m a p map map维护映射元素上一次的出现位置。
- 对于命中缓存的情况,先擦除上次的数据,再保存本次的编号;
- 对于缓存已满的情况,则擦除 s e t set set中的第一个元素(历史最久远的块),然后插入新块;
- 缓存未满,直接插入新块。
有一个技巧,在插入元素时不要使用 i n s e r t insert insert,而是使用 e m p l a c e emplace emplace方法,避免插入时多余的一次构造和析构,可以提升程序效率。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int a[N];
struct node {
int idx, val;
bool operator<(const node& a) const { return idx < a.idx; }
};
inline bool check(int x, int n, int k){
set<node> se;
unordered_map<int, int> mp;
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (mp.count(a[i])) {
++cnt;
se.erase({mp[a[i]], a[i]});
se.insert({i, a[i]});
mp[a[i]] = i;
continue;
}
if (se.size() == x) {
node top = *se.begin();
se.erase(se.begin());
mp.erase(top.val);
}
mp[a[i]] = i;
se.emplace(node{i, a[i]});
}
return cnt >= k;
}
signed main(){
int n, k; cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> a[i];
int l = 1, r = n + 1, ans = -1;
while(l <= r){
int mid = l + r >> 1;
if(check(mid, n, k)) r = mid - 1, ans = mid;
else l = mid + 1;
}
if(ans < 0) puts("cbddl");
else printf("%lld\n", ans);
return 0;
}