using System;
using System.Collections.Generic;


class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public List<int> LFU (List<List<int>> operators, int k) {
        LFUcache cache = new LFUcache(k);
        List<int> res = new List<int>();
        foreach(List<int> op in operators){
            if(op[0] == 1){
                cache.Set(op[1], op[2]);
            }
            else if(op[0] == 2){
                res.Add(cache.Get(op[1]));
            }
        }
        return res;
    }

    class LFUcache{
        Dictionary<int, Node> keyDic = new Dictionary<int, Node>();
        Dictionary<int, DLlist> freqDic = new Dictionary<int, DLlist>();
        readonly int capacity;
        int count => keyDic.Count;
        int minFrequency;
        
        public LFUcache(int capacity){
            this.capacity = capacity;
        }
        public int Get(int key){
            //Console.WriteLine("get:"+ key);
            if(keyDic.TryGetValue(key, out Node node)){
                MoveNodeInList(node);
                return node.Value;
            }
            return -1;
        }

        public void Set(int key, int value){
            Console.WriteLine("set:"+ key +"to" + value);
            if(keyDic.TryGetValue(key, out Node node)){
                node.Value = value;
                return;
            }
            if(count == capacity){
                Node spareNode = freqDic[minFrequency].GetTail();
                freqDic[minFrequency].Remove(spareNode);
                if(freqDic[minFrequency].Count == 0){
                    freqDic.Remove(minFrequency);
                }
                //Console.WriteLine("remove" + spareNode.Key);
                keyDic.Remove(spareNode.Key); 
            }
            Node newNode = new Node(key, value);
            keyDic.Add(key, newNode);
            minFrequency = 1;
            if(!freqDic.ContainsKey(1)){
                //Console.WriteLine("good"+ newNode.Value);
                freqDic.Add(1, new DLlist());
            }
            freqDic[1].AddFirst(newNode);
        }

        void MoveNodeInList(Node node){
            freqDic[node.Frequency].Remove(node);
            if(freqDic[node.Frequency].Count == 0){
                freqDic.Remove(node.Frequency);
                if(minFrequency == node.Frequency){
                    minFrequency++;
                }   
            }
            node.Frequency++;
            if(!freqDic.ContainsKey(node.Frequency)){
                freqDic[node.Frequency] = new DLlist();
            }
            freqDic[node.Frequency].AddFirst(node);
        }


        class Node{
            internal int Frequency{get; set;}
            internal int Key{get; set;}
            internal int Value{get; set;}
            internal Node pre;
            internal Node next;

            internal Node():this(-1,-1){}
            internal Node(int key, int value){
                Key = key;
                Value = value;
                Frequency = 1;
            }
        }

        class DLlist{
            internal Node head = new Node();
            internal Node tail = new Node();
            internal int Frequency{get; set;}
            internal int Count{get;set;}

            internal DLlist(){
                head.next = tail;
                tail.pre = head;
                Count = 0;
            }

            internal void AddFirst(Node node){
                head.next.pre = node;
                node.next = head.next;
                node.pre = head;
                head.next = node;
                Count++;
            }
            internal void Remove(Node node){
                node.pre.next = node.next;
                node.next.pre = node.pre;
                Count--;
            }
            internal Node GetHead(){
                return head.next;
            }
            internal Node GetTail(){
                if(tail.pre == head) Console.WriteLine("cant get tail!!!");
                return tail.pre;
            }
        }

    }
}