Problem:
Score : 400 points

Problem Statement

There are N pieces of sushi. Each piece has two parameters: "kind of topping" t_i and "deliciousness" d_i.
You are choosing K among these N pieces to eat.
Your "satisfaction" here will be calculated as follows:

The satisfaction is the sum of the "base total deliciousness" and the "variety bonus".

The base total deliciousness is the sum of the deliciousness of the pieces you eat.

The variety bonus is x*x, where x is the number of different kinds of toppings of the pieces you eat.

You want to have as much satisfaction as possible.
Find this maximum satisfaction.

Constraints

1 \leq K \leq N \leq 10^5

1 \leq t_i \leq N

1 \leq d_i \leq 10^9

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N K
t_1 d_1
t_2 d_2
.
.
.
t_N d_N

Output

Print the maximum satisfaction that you can obtain.

Sample Input 1

5 3
1 9
1 7
2 6
2 5
3 1

Sample Output 1

26

If you eat Sushi 1,2 and 3:

The base total deliciousness is 9+7+6=22.

The variety bonus is 2*2=4.

Thus, your satisfaction will be 26, which is optimal.

Sample Input 2

7 4
1 1
2 1
3 1
4 6
4 5
4 5
4 5

Sample Output 2

25

It is optimal to eat Sushi 1,2,3 and 4.

Sample Input 3

6 5
5 1000000000
2 990000000
3 980000000
6 970000000
6 960000000
4 950000000

Sample Output 3

4900000016

Note that the output may not fit into a 32-bit integer type.

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;



namespace CSharpSample01
{

    struct Sushi
    {
        public int t;  //ネタID
        public int d;  //おいしさ
        public Sushi(int neta, int oisisa)
        {
            t = neta;
            d = oisisa;
        }
    }

    //Sushiノードの優先度付きキュー
    //おいしさdが対象
    class PriorityQueue_Sushi
    {
        int H;
        //高速に要素にアクセスするためにリストでなく配列
        Sushi[] heap;
        const int MAX = 2000000;
        bool is_maxheap;

        //f:最大ヒープにするか(true:Max, false:Min)
        public PriorityQueue_Sushi(bool f)
        {
            H = 0;
            heap = new Sushi[MAX];
            is_maxheap = f;
        }

        public PriorityQueue_Sushi(int size, bool f)
        {
            H = size;
            heap = new Sushi[H + 1];
            is_maxheap = f;
        }

        //上のuから下までずーと(入れ替えられなくなるor限界Hまで)
        //p:=MaxHeapfyを始めるノード番号
        public void MaxHeapfy(int p)
        {
            while (2 * p <= H)
            {
                int son_n = 2 * p;
                Sushi son = heap[2 * p];
                if (2 * p + 1 <= H && son.d < heap[2 * p + 1].d)
                {
                    son = heap[2 * p + 1];
                    son_n++;
                }

                if (son.d > heap[p].d)
                {
                    Sushi tmp = heap[p];
                    heap[p] = son;
                    heap[son_n] = tmp;
                }
                else
                {
                    break;
                }

                p = son_n;
            }
        }

        public void MinHeapfy(int p)
        {
            while (2 * p <= H)
            {
                int son_n = 2 * p;
                Sushi son = heap[2 * p];
                if (2 * p + 1 <= H && son.d > heap[2 * p + 1].d)
                {
                    son = heap[2 * p + 1];
                    son_n++;
                }

                if (son.d < heap[p].d)
                {
                    Sushi tmp = heap[p];
                    heap[p] = son;
                    heap[son_n] = tmp;
                }
                else
                {
                    break;
                }

                p = son_n;
            }
        }

        public void BuildMaxHeap()
        {
            for (int s = H / 2; s >= 1; s--)
            {
                MaxHeapfy(s);
            }
        }

        public void BuildMinHeap()
        {
            for (int s = H / 2; s >= 1; s--)
            {
                MinHeapfy(s);
            }
        }

        public void Insert(Sushi key)
        {
            //下から上にずーとヒープ
            H++;
            heap[H] = key;

            int son = H;

            while (son != 1)
            {
                int p = son / 2;
                if (is_maxheap && heap[son].d > heap[p].d)
                {
                    Sushi tmp = heap[p];
                    heap[p] = heap[son];
                    heap[son] = tmp;
                    son /= 2;
                }
                else if (!is_maxheap && heap[son].d < heap[p].d)
                {
                    Sushi tmp = heap[p];
                    heap[p] = heap[son];
                    heap[son] = tmp;
                    son /= 2;
                }
                else
                {
                    break;
                }
            }

        }

        public Sushi Extract()
        {
            Sushi max = heap[1];
            heap[1] = heap[H];
            H--;

            if (is_maxheap) MaxHeapfy(1);
            else MinHeapfy(1);

            return max;
        }

        public int Count()
        {
            return H;
        }

    }

    class Program
    {
        static int N;
        static int K;
        static PriorityQueue_Sushi stomach;
        static PriorityQueue_Sushi not_eat;
        //t_cnt[i] := ネタiを何個食べたか
        static int[] t_cnt;
        //t_max[i] := ネタiのすしのおいしさdの最大値
        static int[] t_max;
        //入力されたすしを格納するsushi_list
        static List<Sushi> sushi_list;
        //ソートしたsushi_listの中のおいしさ上位K個のeat_sushi_list
        static List<Sushi> eat_sushi_list;

        static long max_point;
        static long d_sum;
        static long now_type_cnt;

        static void Create_stomach()
        {
            //最初に食べるK個のすしに対して
            //stamach生成(d_sumとnow_type_cntの更新)
            //Console.WriteLine("eat_sushi_list");
            for (int i = 0; i < K; i++)
            {
                Sushi sushi = eat_sushi_list[i];
                //Console.WriteLine($"t:{sushi.t} d:{sushi.d}");
                d_sum += sushi.d;
                if (t_cnt[sushi.t] >= 1)
                {
                    stomach.Insert(sushi);
                }
                if (t_cnt[sushi.t] == 0)
                {
                    now_type_cnt++;
                }
                t_cnt[sushi.t]++;
            }
        }

        //not_eat生成
        static void Create_not_eat()
        {
            //t_max更新
            //Console.WriteLine("not_eat_list");
            for (int i = K; i < N; i++)
            {
                Sushi sushi = sushi_list[i];
                //Console.WriteLine($"t:{sushi.t} d:{sushi.d}");
                if (t_cnt[sushi.t] == 0)
                {
                    t_max[sushi.t] = Math.Max(t_max[sushi.t], sushi.d);
                }
            }
            //t_max > 0のネタをnot_eatに入れる
            for (int i = 1; i <= N; i++)
            {
                if (t_max[i] > 0) not_eat.Insert(new Sushi(i, t_max[i]));
            }
        }

        //max_pointを更新
        static void Updata_max_point()
        {      
            while (true)
            {
                if (now_type_cnt == K || stomach.Count() == 0 || not_eat.Count() == 0) break;

                Sushi sushi = stomach.Extract();
                d_sum -= sushi.d;

                sushi = not_eat.Extract();
                d_sum += sushi.d;

                ++now_type_cnt;

                max_point = Math.Max(max_point, d_sum + now_type_cnt * now_type_cnt);

            }
        }

        static void Main(string[] args)
        {
            //N, Kの入力
            string[] input = Console.ReadLine().Split(' ');
            N = int.Parse(input[0]);
            K = int.Parse(input[1]);

            t_cnt = new int[N + 1];
            t_max = new int[N + 1];
            sushi_list = new List<Sushi>();
            eat_sushi_list = new List<Sushi>();
            stomach = new PriorityQueue_Sushi(false);  //Minヒープ
            not_eat = new PriorityQueue_Sushi(true);  //Maxヒープ

            //すし(ネタ, おいしさ)を入力
            for (int i = 0; i < N; i++)
            {
                input = Console.ReadLine().Split(' ');
                int t = int.Parse(input[0]);
                int d = int.Parse(input[1]);
                sushi_list.Add(new Sushi(t, d));
            }

            //sushi_listをdでソート
            sushi_list = sushi_list.OrderByDescending(x => x.d).ToList();

            //上位K個を選ぶ
            for (int i = 0; i < K; i++) eat_sushi_list.Add(sushi_list[i]);
            eat_sushi_list = eat_sushi_list.OrderBy(x => x.t).ThenByDescending(x => x.d).ToList();

            /*
            Console.WriteLine("eat_sushi_list");
            foreach (var s in eat_sushi_list) Console.WriteLine($"t:{s.t} d:{s.d}");
            */

            max_point = 0;
            d_sum = 0;
            now_type_cnt = 0;

            //stomach生成
            Create_stomach();

            //max_pointの初期化
            max_point = d_sum + now_type_cnt * now_type_cnt;
            //Console.WriteLine($"max_point初期値:{max_point}");

            //not_eat生成
            Create_not_eat();

            //max_pointを求める
            Updata_max_point();

            Console.WriteLine(max_point);
        }
    }
}