[JSOI2010]缓存交换 贪心 & 堆
题解:
首先我们要使得Miss的次数尽量少,也就是要尽量保证每个点在被访问的时候,这个点已经存在于Cache中。
那么我们可以得到一个结论:
如果Cache已满,那么我们就从Cache里面取出下一个出现位置最远的那个数(如果不出现了那么令下一个出现位置为inf)
所以用堆维护即可。
我们记录一个Next[i],表示s[i]后面再次遇到s[i]的位置是哪,如果当前的s[i]就是最后一个s[i],那么Next[i] = inf
值得注意的是,遇到相同元素时我们并不需要从堆中删除上一个数然后再塞入当前数,因为这将不会对操作产生影响。而且要强行删除的话要重建整个堆,时间复杂度承受不来。。。
为什么不会有影响?
注意到对于相同元素而言,Next[i]是递增的
即Next[i] < Next[Next[i]] < Next[Next[Next[i]]]
而遇到相同元素就代表遇到了Next[i],所以塞入这个数后,上一个数虽然没有被删除,但被这个数覆盖了,每次取出肯定是优先这个数的
(为什么突然觉得有点不太严谨。。。。那为了保险的话就在取数的时候判断一下就好了)
1 // luogu-judger-enable-o2 2 #include<bits/stdc++.h> 3 using namespace std; 4 #define R register int 5 #define AC 100100 6 #define inf 10000000 7 int n, m, tot, cnt, ans; 8 int s[AC], last[AC], w[AC], Next[AC]; 9 bool z[AC]; 10 11 struct cmp{ 12 bool operator () (int a, int b) 13 { 14 return Next[a] < Next[b]; 15 } 16 }; 17 18 priority_queue<int, vector<int>, cmp> q; 19 20 inline int read() 21 { 22 int x = 0; char c = getchar(); 23 while(c > '9' || c < '0') c = getchar(); 24 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 25 return x; 26 } 27 28 inline bool cmp(int a, int b) 29 { 30 return a < b; 31 } 32 33 int half(int x)//二分离散化后的编号 34 { 35 int l = 1, r = tot, mid; 36 while(l < r) 37 { 38 mid = (l + r) >> 1; 39 if(w[mid] < x) l = mid + 1; 40 else if(w[mid] > x) r = mid - 1; 41 else if(w[mid] == x) return mid; 42 } 43 return l; 44 } 45 46 void pre() 47 { 48 int x; 49 n = read(), m = read(); 50 for(R i = 1; i <= n; i++) s[i] = w[i] = read(); 51 sort(w + 1, w + n + 1, cmp); 52 for(R i = 1; i <= n; i++) 53 if(w[i] != w[i+1]) w[++tot] = w[i]; 54 for(R i = 1; i <= n; i++) 55 { 56 x = half(s[i]); 57 if(last[x]) Next[last[x]] = i;//建立一个单向的连接 58 last[x] = i;//记录上一个出现的x的位置 59 } 60 for(R i = 1; i <= n; i++) 61 if(!Next[i]) Next[i] = inf; 62 }//最后一个的Next为inf(最优先弹出) 63 64 void work() 65 {//因为要弹出的是最远的,而之前的相比之后的必然要近一些(同一个数字),而且每次被迫弹出后都必然会塞入一个更远的, 66 int x, id;//所以之前那些东西放在堆里其实也没关系,不会对答案产生影响 67 for(int i = 1; i <= n; i++) 68 { 69 id = half(s[i]);//获取编号 70 if(z[id]) 71 { 72 q.push(i); 73 continue;//如果已经有了,,,那也要赛进去,,,表示替换对应的Next,但不能计入ans 74 }//因为不删去旧元素并不会造成影响,因此还是可以视作q内没有重复元素的 75 else if(cnt < m) //如果还没有满 76 { 77 ++cnt, ++ans; 78 q.push(i); 79 z[id] = true; 80 } 81 else 82 { 83 x = q.top(); 84 x = half(s[x]);//存的是编号 85 z[x] = false;//已经弹出了 86 q.pop(); 87 q.push(i); 88 z[id] = true; 89 ++ans; 90 } 91 } 92 printf("%d\n", ans); 93 } 94 95 int main() 96 { 97 //freopen("in.in", "r", stdin); 98 pre(); 99 work(); 100 // fclose(stdin); 101 return 0; 102 }