codevs2069 油画 — 动态维护优先队列

先来看题

题目描述 Description
yh最近迷上了油画 。一天,好朋友ywm来到yh家,yh便决定当场画上一幅。
完成一幅油画需要涂色n次,每次需要用的颜色编号为a[i],一种颜色可能被多次使用。
yh的调色盘大小有限,最多只能存放m种不同色颜料,且初始时颜料盘中装有的颜料编号为1,2,…,m。
每次涂色时,如果当前需要的颜料恰好在yh的调色盘中,yh可以继续画。否则的话,yh需要朋友ywm帮忙取来当前所需的颜料,并考虑在完成本次涂色后,是否保留该种颜色的颜料。如果要保留,则需要从调色盘已有的颜色中选出一种洗掉,来为新加入的颜色空出位置。
由于ywm是客人,yh不想总是麻烦他,于是,他想让你帮忙计算,最少让ywm帮忙多少次可以完成yh的油画?
输入描述 Input Description
第一行两个整数,n,m,完成油画共要涂色n次;调色盘最多能装m种不同颜色的颜料,且初始的颜料编号为1-m。
第二行共n个数,表示第i次需要用的颜色a[i]。
输出描述 Output Description
输出1行,ywm需要帮忙的最少次数。
样例输入 Sample Input
11 3
4 1 2 1 5 3 4 4 1 2 3
样例输出 Sample Output
4
数据范围及提示 Data Size & Hint
对于100%的数据,n≤200000,m≤100000,a[i] ≤2000000

此题做法是贪心,每次拿走下一次出现最晚的那个(记为D)。
那么我们就要支持2种操作:
1:查找盘内的D
2:插入、删除、更改盘内元素
然后就很顺利地想到了优先队列(就是堆)。好歹也不是道水题考到了手写堆。大根堆里面存储(i,j),表示i盘内颜色i,下一次出现的位置为j。
然后讲一讲操作:
1:开始的时候向堆里面插入1~m号元素;
2:从1推到n,如果当前元素在盘里面,就把这个元素的j修改并维护堆。由于j一定单调递增所以只需要向上维护。
3:如果当前元素不在盘里面,就把它的j和堆顶的元素的j比较然后决定是否替换、维护。
总复杂度大概是O(nlogn)。
堆写的巨丑不要在意。
代码如下:

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#define ls (x << 1)
#define rs (x << 1 | 1)
#define fa (x >> 1)
#define MAX 2147483647
//  QAEAWAA W:下一个是第几个  color:颜色 
using namespace std;
struct QAEAWAA{int W,color;};
int n,m,to[2010100],next[2010000],first[2010000],tot,Ans,inQ[2010000],place[2010000];
QAEAWAA a[2001000],Q[2020000];
inline int gi()
{
    int x=0;char ch=getchar();
    while(ch>'9' || ch<'0')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
    return x;
}
inline void push(QAEAWAA c)
{
    Q[++tot]=c;QAEAWAA ch;place[c.color]=tot;
    for(int x=tot;fa && Q[x].W>Q[fa].W;x=fa)
    {
        swap(place[Q[x].color],place[Q[fa].color]);
        ch=Q[x],Q[x]=Q[fa],Q[fa]=ch;
    }
}
void pop()
{
    inQ[Q[1].color]=0;place[Q[1].color]=0;Q[1]=Q[tot--];QAEAWAA ch;Q[tot+1]=ch;int x;
    for(x=1;rs<=tot;)
    {
        if(Q[x].W>=Q[ls].W && Q[x].W>=Q[rs].W)break;
        if(ls==tot)
        {
            if(Q[x].W>=Q[ls].W)break;
            swap(place[Q[x].color],place[Q[ls].color]);
            ch=Q[x];Q[x]=Q[ls];Q[ls]=ch;
            break;
        }
        else
        {
            if(Q[ls].W>Q[rs].W)
            {
                swap(place[Q[x].color],place[Q[ls].color]);
                ch=Q[x];Q[x]=Q[ls];Q[ls]=ch;x=ls;
            }
            else
            {
                swap(place[Q[x].color],place[Q[rs].color]);
                ch=Q[x];Q[x]=Q[rs];Q[rs]=ch;x=rs;
            }
        }
    }
}
void deal(int i)
{
    QAEAWAA now=a[i],ch;int x=place[now.color];
    Q[x].W=a[i].W;
    for(;fa && Q[x].W>Q[fa].W;x=fa)
    {
        swap(place[Q[x].color],place[Q[fa].color]);
        ch=Q[x],Q[x]=Q[fa],Q[fa]=ch;
    }
}
inline void work()
{
    n=gi(),m=gi();
    for(int i=1;i<=2000000;++i)first[i]=n+1;
    for(int i=1;i<=n;++i)
    {
        int k=gi();
        a[i].color=k;a[i].W=n+1;
        if(to[k]==0)first[k]=i;
        a[to[k]].W=i;to[k]=i;
    }
    for(int i=1;i<=m;++i)
    {
        QAEAWAA k;k.W=first[i];k.color=i;
        push(k);inQ[i]=1;
    }
    for(int i=1;i<=n;++i)
    {
        if(inQ[a[i].color])deal(i);
        else{Ans++;if(a[i].W<Q[1].W){pop();push(a[i]);inQ[a[i].color]=1;}}
    }
}
int main()
{
    work();
    cout<<Ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值