技巧
    堆 贪心
思路
    如何贪心?
        如果缓存区满了并且当前元素缓存里面不存在。 那么需要感知到当前缓存里面最远再次需要使用的元素进行淘汰。
   如何实现?
        用堆记录着元素下次出现的位置i 作为大根堆的权重 那么每次只需要要对堆顶做文章即可
实现
package main

import (
	"bufio"
	. "fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

type Cache struct {
	nextIndex, num, pos int
}

func siftUp(heap []*Cache, index int) {
	for index != 0 && heap[index].nextIndex > heap[(index-1)/2].nextIndex {
		heap[index].pos, heap[(index-1)/2].pos = heap[(index-1)/2].pos, heap[index].pos
		heap[index], heap[(index-1)/2] = heap[(index-1)/2], heap[index]
		index = (index - 1) / 2
	}
}

func siftDown(heap []*Cache, last int) {
	curr := 0
	for curr*2+1 < last {
		max := curr
		if heap[curr*2+1].nextIndex > heap[curr].nextIndex {
			max = curr*2 + 1
		}
		if curr*2+2 < last && heap[curr*2+2].nextIndex > heap[max].nextIndex {
			max = curr*2 + 2
		}
		if max == curr {
			break
		}
		heap[curr].pos, heap[max].pos = heap[max].pos, heap[curr].pos
		heap[curr], heap[max] = heap[max], heap[curr]
		curr = max
	}
}

// 获取i后一个位置
func getNextIndex(tmp map[int][]int, num int, i int) int {
	if i == int(^uint(0)>>1) {
		return int(^uint(0) >> 1)
	}
	l, r := 0, len(tmp[num])-1
	for l <= r {
		mid := l + (r-l)/2
		if tmp[num][mid] <= i {
			l = mid + 1
		} else {
			r = mid - 1
		}
	}
	if l == len(tmp[num]) {
		return int(^uint(0) >> 1)
	}
	return tmp[num][l]
}

// https://ac.nowcoder.com/acm/problem/20185
func NC20185(_r io.Reader, _w io.Writer) {
	in, out := bufio.NewReader(_r), bufio.NewWriter(_w)
	defer out.Flush()
	line1, _ := in.ReadString('\n')
	line1 = strings.Trim(line1, "\n")
	line2, _ := in.ReadString('\n')
	line2 = strings.Trim(line2, "\n")
	first := strings.Split(line1, " ")
	second := strings.Split(line2, " ")
	n, _ := strconv.Atoi(first[0])
	m, _ := strconv.Atoi(first[1])
	arr := make([]int, n)
	tmp := make(map[int][]int) // 每个元素的索引位置
	for i := 0; i < n; i++ {
		num, _ := strconv.Atoi(second[i])
		arr[i] = num
		if _, ok := tmp[arr[i]]; ok {
			tmp[arr[i]] = append(tmp[arr[i]], i)
		} else {
			tmp[arr[i]] = []int{i}
		}
	}
	ans := 0
	heap, lastIndex := make([]*Cache, m), 0 // 大根堆
	currHeapCache := make(map[int]*Cache)   // 缓存 (O1) 复杂度 这里不做缓存AC不掉

	for i := 0; i < n; i++ {
		// 如果已经在缓存中存在
		if c, ok := currHeapCache[i]; ok {
			delete(currHeapCache, c.nextIndex)
			c.nextIndex = getNextIndex(tmp, arr[i], i) // 更新下一个位置
			currHeapCache[c.nextIndex] = c
			siftUp(heap, c.pos)
		} else {
			if lastIndex == m { // 淘汰堆顶
				delete(currHeapCache, heap[0].nextIndex)
				heap[0] = &Cache{getNextIndex(tmp, arr[i], i), arr[i], 0}
				currHeapCache[heap[0].nextIndex] = heap[0]
				siftDown(heap, lastIndex)
			} else {
				// 堆还没满
				heap[lastIndex] = &Cache{getNextIndex(tmp, arr[i], i), arr[i], lastIndex}
				currHeapCache[heap[lastIndex].nextIndex] = heap[lastIndex]
				siftUp(heap, lastIndex)
				lastIndex++
			}
			ans++
		}
	}
	Fprintln(out, ans)
}

func main() {
	NC20185(os.Stdin, os.Stdout)
}