1. 程式人生 > >BZOJ1528: [POI2005]sam-Toy Cars

BZOJ1528: [POI2005]sam-Toy Cars

ans 而且 lin sim urn 一個 ... 可能 策略

一開始是有不少 simple 的想法的...

比如剩余使用次數越多越優或者越少越劣這樣

隨便找找反例發現這顯然是不行的

比如一個點之前用的很少但在最後用的很多,
如果我還一直保留他那中間過程中的可用位置就變少了
可能會導致很多出現次數較少但比較集中的物品被反復拿多次

針對這種情況,有一個貪心策略就是下次出現的越早的物品留著越優

貌似也沒什麽反例(我也不會證明

這樣就每個需求記錄一下下一次出現的位置

用堆維護,在堆滿時將最劣的物品彈掉

這時我非常激動地無腦碼就 GG 成 10 pts 了

在仔細考慮會不會出鍋後,會發現如果在需要一個物品而它又恰好在堆中時,
什麽操作也不用做,但在這之後這個物品就永遠地留在了堆裏,

而且他永遠不會成為堆頂(k = 1 不影響)

所以應該每次發現當前需求物品在堆中後 ++k


代碼:

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <queue>
using namespace std;

const int MAXN = 100005, MAXP = 500005;

struct ITEM{
    int nxt, id;
    ITEM(int NXT = 0, int ID = 0) {nxt = NXT; id = ID;}
    bool operator < (const ITEM& b) const {
        return nxt < b.nxt;
    }
};
int n, k, p, ans;
int req[MAXP], pos[MAXN], nxt[MAXP];
bool inq[MAXN];
priority_queue<ITEM> q;

inline int rd() {
    register int x = 0;
    register char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) {
        x = x * 10 + (c ^ 48);
        c = getchar();
    }
    return x;
}

int main() {
    n = rd(); k = rd(); p = rd();
    for (int i = 1; i <= p; ++i) 
        req[i] = rd();
    for (int i = p; i; --i) {
        nxt[i] = ((pos[req[i]]) ? pos[req[i]] : (p + 1));
        pos[req[i]] = i;
    }
    for (int i = 1; i <= p; ++i) {
        if (!inq[req[i]]) {
            if (q.size() == k) {
                inq[q.top().id] = false;
                q.pop();
            }
            q.push(ITEM(nxt[i], req[i]));
            inq[req[i]] = true;
            ++ans;
        } else {
            ++k;
            q.push(ITEM(nxt[i], req[i]));
        }
    }
    printf("%d\n", ans);
    return 0;
}

BZOJ1528: [POI2005]sam-Toy Cars