1. 程式人生 > >BZOJ4837:[Lydsy1704月賽]LRU演算法(雙指標&模擬)

BZOJ4837:[Lydsy1704月賽]LRU演算法(雙指標&模擬)

Description

小Q同學在學習作業系統中記憶體管理的一種頁面置換演算法,LRU(LeastRecentlyUsed)演算法。 為了幫助小Q同學理解這種演算法,你需要在這道題中實現這種演算法,接下來簡要地介紹這種演算法的原理: 1.初始化時,你有一個最大長度為n的空佇列,用於暫時儲存一些頁面的地址。 2.當系統需要載入一個不在佇列中的頁面時,如果佇列已滿,則彈出隊首元素,並將需要載入的頁面加到隊尾, 否則直接將需要載入的頁面加到隊尾。 3.當系統需要載入一個在佇列中的頁面時,將該頁面移至隊尾。 在這道題中,小Q同學需要處理有q個請求,每個請求會給定一個整數x,表示系統需要載入地址為x的頁面, 而你需要在每個請求完成後給出整個佇列中頁面的地址之和。為了便於計算,設第i個請求給出的整數為x_i, 第i個請求後你給出的答案為y_i,則對於1<i≤q有x_i=(A*x_(i-1)+B)modp,其中x_1,A,B,p是給 定的整數,並且你只需要輸出sigma(i*y_i),1<=i<=Q對2^64取模的值,而不是每個y_i。

Input

第一行包含一個正整數T,表示有T組資料,滿足T≤20。 接下來依次給出每組測試資料。對於每組測試資料: 第一行包含兩個正整數n和q,滿足1≤n≤10^5,1≤q≤10^6。 第二行包含四個整數x_1,A,B和p,滿足0≤x_1,A,B<p,1≤p≤10^6+3。

Output

 對於每組測試資料,輸出一行一個非負整數,表示這組資料的答案。

Sample Input

2
5 10
0 1 1 5
5 10
0 1 1 10

Sample Output

485
1135

思路:加入X,如果X不存在佇列,當佇列裡小於N個,直接加到隊尾;否則刪去隊首刪去隊尾。 如果X存在隊裡,那麼移到隊尾。  我們發現用佇列不方便模擬,因為無法刪去中間的數,而set來操作又會超時。   因為我們維護的是一個長度為N的視窗,可以用雙指標來模擬。 維護一個佇列q,如果q[i]=-1,說明位置i已經被刪去。同時維護一個pos陣列保儲存其在佇列的位置。 最後用一個隊首head來維護現在刪到了哪裡。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000010;
ll a[maxn],A,B,P; int q[maxn],pos[maxn],cnt,head,num;
unsigned long long ans,res;
int main()
{
    int T,N,Q;
    scanf("%d",&T);
    while(T--){
        cnt=0; ans=0; res=0; head=1; num=0;
        scanf(
"%d%d",&N,&Q); scanf("%lld%lld%lld%lld",&a[1],&A,&B,&P); for(int i=2;i<=Q;i++) a[i]=(A*a[i-1]+B)%P; for(int i=1;i<=Q;i++) pos[a[i]]=-1; for(int i=1;i<=Q;i++){ if(pos[a[i]]==-1){ if(num<N) { num++; pos[a[i]]=++cnt, q[cnt]=a[i],res+=a[i]; } else { while(q[head]==-1) head++; pos[q[head]]=-1,res-=q[head],q[head]=-1; pos[a[i]]=++cnt,q[cnt]=a[i],res+=a[i]; } } else { q[pos[a[i]]]=-1,pos[a[i]]=++cnt,q[cnt]=a[i]; } ans+=res*i; } cout<<ans<<endl; } return 0; }