bzoj 1206 //1206: [HNOI2005]虚拟内存 map set stl/离散化+线段树

bzoj 1206 //1206: [HNOI2005]虚拟内存   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1206

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

map set  stl

//1206: [HNOI2005]虚拟内存
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1206
//模拟算法的时间复杂度O(n*m) 10^10超时无疑
//涉及查找特定数据,涉及数据中的最小访问次数,最早进入内存时间.
//要考虑的东西比较多.通常的堆只射击最小或最大值,而不涉及查找某个特定数据2019-8-31 19:55
//此文https://www.cnblogs.com/BriMon/p/9366094.html代码写得不错2019-8-31 22:04
//set用法详见https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/13/2636375.html
//set重载 '<' 运算符可见https://www.cnblogs.com/yaoyueduzhen/p/4536929.html
//map用法详见https://blog.csdn.net/shuzfan/article/details/53115922
//因其中第i+1行有一个正整数Pi<=10^9,m<1000000,采用桶排序,数组开不出,故采用map,也算是升级版数组
//set是堆,需重载<
//编写过程中,set编写还是有些问题,不过,还是编码成功了。
//样例通过,提交AC.2019-9-3
//以下代码,为采用STL编写。
#include <cstdio>
#include <map>
#include <set>
#define maxm 1000010
using namespace std;
struct data{
    int seq,num,time;//seq虚拟内存页的编号,num访问次数,time进入内存时间
}d;
map<int,int> mp1,mp2;//mp1对应seq在内存中的访问次数,mp2对应seq进入内存时间
set<data> s;//s堆的功能
bool operator < (data a,data b){
    if(a.num==b.num)return a.time<b.time;
    return a.num<b.num;
}
int main(){
    int n,m,i,ans=0,now=0,seq;//now当前内存中已占用的页面数
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d",&seq);
        if(mp1[seq]){//在内存
            ans++;
            d.seq=seq,d.num=mp1[seq],d.time=mp2[seq];
            s.erase(d);
            mp1[seq]++,d.num=mp1[seq];
            s.insert(d);
        }else{//不在内存中
            if(now==n){//内存已用完
                d=*s.begin();//此处错写成d=s.begin();//此处错写成d=*it.begin();
                s.erase(d);
                mp1[d.seq]=0,mp2[d.seq]=0;
                mp1[seq]=1,mp2[seq]=i,d.seq=seq,d.num=mp1[seq],d.time=mp2[seq];
                s.insert(d);
            }else{//内存未用完
                now++;//当前内存中已占用的页面数
                mp1[seq]=1,mp2[seq]=i;
                d.seq=seq,d.num=mp1[seq],d.time=mp2[seq];
                s.insert(d);
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

离散化+线段树

//1206: [HNOI2005]虚拟内存
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1206
//线段树此文https://www.cnblogs.com/AKMer/p/9106026.html写得不错
//该题用线段树编写,感觉体量挺大,工程复杂。2019-9-5
//样例通过,提交AC.2019-9-5 21:26
//自我感觉,change函数,编写难度较大
//线段树操作,此题还是收获颇丰,自根向叶更新,自叶向根更新.
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 10010
#define maxm 1000010
#define INF 2000000000
using namespace std;
int cnt=1,n,m,inseq[maxm];//inseg[v]表示v号页在线段树的叶节点的编号是多少 inseq[rk] rk值 对应在线段树叶节点上的位置inseq[rk]
struct node{
    int v,seq,rk;//v虚拟内存页的编号,seq读入序列,rk离散化后的值
}a[maxm];
struct treenode{
    int rk,num,time,pos;//此处错写成rk,num,time,pos;漏了int//rk对应离散化后的编号,num访问次数,time进入内存时间,pos属结构体数组a中元素位置,
}tree[maxn*4];//线段树 叶结点对应内存中页面 //二叉树节点 叶节点 也即 内存节点
int cmp_v(node a,node b){
    return a.v<b.v;
}
int cmp_seq(node a,node b){
    return a.seq<b.seq;
}
bool operator <(treenode a,treenode b){//访问次数为第一关键字,进入内存时间为第二关键字
    if(a.num==b.num)return a.time<b.time;
    return a.num<b.num;
}
void update(int k){//子节点 更新 父节点,不递归,省时间//神奇的一步,用在线段树上,更新的节点 是 非树叶节点//由 子节点 更新 父节点 此处不用递归,递归 由调用它的函数实现
    if(tree[2*k]<tree[2*k+1])tree[k]=tree[2*k];//哪边的替换优先级更高就用哪边更新父亲
    else tree[k]=tree[2*k+1];
}
void build(int k,int L,int R){//k线段树中位置 L结构体数组a左边界 R结构体数组a右边界
    int mid=(L+R)/2;
    if(L==R){//叶节点
        tree[k].rk=INF,tree[k].pos=L,tree[k].num=tree[k].time=0;
        return;
    }
    build(2*k,L,mid);
    build(2*k+1,mid+1,R);
    update(k);//由 子节点 更新 父节点k,保证 线段树 根节点是最早,访问次数最小的数据.
}
void add(int k,int isLeaf){//isLeaf是否叶节点,该函数自叶节点开始,更新到根节点结束
    if(isLeaf)tree[k].num++;//更新叶节点
    else update(k);//因叶节点肯定更新了,开始更新非叶节点
    if(k==1)return;//根节点 递归结束
    add(k/2,0);//因要更新非叶节点,故需递归
}
void change(int k,int L,int R,int rk,int time){//替换根节点,更新整棵树变化的部分.从 根节点 向 叶节点 更新
    int mid=(L+R)/2;
    if(L==R){
        if(tree[k].rk!=INF)inseq[tree[k].rk]=0;//把原先占据该结点的页信息抹掉//更新该处被占用的叶节点
        inseq[rk]=k;
        tree[k].rk=rk,tree[k].num=1,tree[k].time=time,tree[k].pos=L;
        return;
    }
    if(tree[k].pos<=mid)change(2*k,L,mid,rk,time);
    else change(2*k+1,mid+1,R,rk,time);
    update(k);//因子节点更新,故父节点也需更新.
}
int main(){
    int i,j,ans=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)scanf("%d",&a[i].v),a[i].seq=i;//离线之前的读取
    sort(a+1,a+1+m,cmp_v),a[0].v=0;
    for(i=1;i<=m;i++)
        if(a[i].v==a[i-1].v)a[i].rk=cnt;
        else a[i].rk=++cnt;
    sort(a+1,a+1+m,cmp_seq);//离散化 将10^9大小的数据 处理成 10000以内数据
    build(1,1,n);//建树,虽然还没开始读取,先把空间开出来.
    memset(inseq,0,sizeof(inseq));
    for(i=1;i<=m;i++)
        if(inseq[a[i].rk])add(inseq[a[i].rk],1),ans++;//在内存中
        else change(1,1,n,a[i].rk,i);
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值