Problem:
Score: 400 points

Problem Statement

We have N integers A_1, A_2, ..., A_N.

There are \frac{N(N-1)}{2} ways to choose two of them and form a pair. If we compute the product of each of those pairs and sort the results in ascending order, what will be the K-th number in that list?

Constraints

All values in input are integers.

2 \leq N \leq 2 \times 10^5

1 \leq K \leq \frac{N(N-1)}{2}

-10^9 \leq A_i \leq 10^9\ (1 \leq i \leq N)

Input

Input is given from Standard Input in the following format:

N K
A_1 A_2 \dots A_N

Output

Print the answer.

Sample Input 1

4 3
3 3 -4 -2

Sample Output 1

-6

There are six ways to form a pair. The products of those pairs are 9, -12, -6, -12, -6, 8.

Sorting those numbers in ascending order, we have -12, -12, -6, -6, 8, 9. The third number in this list is -6.

Sample Input 2

10 40
5 4 3 2 -1 0 0 0 0 0

Sample Output 2

6

Sample Input 3

30 413
-170202098 -268409015 537203564 983211703 21608710 -443999067 -937727165 -97596546 -372334013 398994917 -972141167 798607104 -949068442 -959948616 37909651 0 886627544 -20098238 0 -948955241 0 -214720580 277222296 -18897162 834475626 0 -425610555 110117526 663621752 0

Sample Output 3

448283280358331064

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

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

class Program
{
    static void Main(string[] args)
    {
        var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
        Console.SetOut(sw);
        Solve();
        Console.Out.Flush();
    }
    static void Solve()
    {
        var N = sc.ReadInt();
        var K = sc.ReadLong();
        var A = sc.ReadLongArray(N);

        //var list = new List<long>();
        //for (int i = 0; i < N; i++)
        //{
        //    for (int j = i + 1; j < N; j++)
        //    {
        //        list.Add(A[i] * A[j]);
        //    }
        //}
        //list.Sort();
        //foreach (var item in list)
        //{
        //    Console.WriteLine(item);
        //}

        var ma = A.Where(a => a < 0).ToArray();
        var mc = ma.Length;
        var pa = A.Where(a => a > 0).ToArray();
        var pc = pa.Length;
        var za = A.Where(a => a == 0).ToArray();
        var zc = za.Length;


        // 答えが正の数かゼロか負の数かで場合分け
        if ((long)mc * pc >= K)
        {
            // 絶対値降順にしておく
            Array.Sort(ma);
            Array.Sort(pa);
            Array.Reverse(pa);

            // 積がX以下になるペアの個数がK個以下になるときの最小のX
            var l = -((long)1e18 + 1);
            var r = -1L;
            while (r - l > 1)
            {
                // mid以下になるペアの個数を数え上げる
                var count = 0L;
                var mid = l + (r - l) / 2;
                for (int i = 0; i < pa.Length; i++)
                {
                    // p*m <= mid
                    var lx = -1;
                    var rx = ma.Length;
                    while (rx - lx > 1)
                    {
                        var midx = lx + (rx - lx) / 2;
                        var mul = pa[i] * ma[midx];
                        if (mul > mid) rx = midx;
                        else lx = midx;
                    }
                    count += rx;
                }
                if (count >= K) r = mid;
                else l = mid;
            }
            Console.WriteLine(r);
            return;
        }
        K -= (long)mc * pc;
        var zc2 = (long)zc * (N - 1) - (long)zc * (zc - 1) / 2;
        if (zc2 >= K)
        {
            Console.WriteLine(0);
            return;
        }
        K -= zc2;

        // 絶対値昇順にしておく
        Array.Sort(ma);
        Array.Reverse(ma);
        Array.Sort(pa);
        {
            // 積がX以下になるペアの個数がK個以下になるときの最小のX
            var l = 0L;
            var r = (long)1e18 + 1;
            while (r - l > 1)
            {
                var count = 0L;
                var mid = l + (r - l) / 2;
                // 正の数*正の数
                for (int i = 0; i < pa.Length; i++)
                {
                    var lx = i;
                    var rx = pa.Length;
                    while (rx - lx > 1)
                    {
                        var midx = lx + (rx - lx) / 2;
                        if (pa[i] * pa[midx] <= mid) lx = midx;
                        else rx = midx;
                    }
                    count += rx - i - 1;
                }
                // 負の数*負の数
                for (int i = 0; i < ma.Length; i++)
                {
                    var lx = i;
                    var rx = ma.Length;
                    while (rx - lx > 1)
                    {
                        var midx = lx + (rx - lx) / 2;
                        if (ma[i] * ma[midx] <= mid) lx = midx;
                        else rx = midx;
                    }
                    count += rx - i - 1;
                }
                if (count < K) l = mid;
                else r = mid;
            }
            Console.WriteLine(r);
        }
    }
    static Scanner sc = new Scanner();
}
class Scanner
{
    string[] S = new string[0];
    int Index = 0;
    char[] Separators = new char[] { ' ' };

    public string Next()
    {
        if (this.Index < this.S.Length) return this.S[this.Index++];
        var line = "";
        while (line == "") line = Console.ReadLine();
        this.S = line.Split(this.Separators, StringSplitOptions.RemoveEmptyEntries);
        if (this.S.Length == 0) return this.Next();
        this.Index = 0;
        return this.S[this.Index++];
    }
    public string ReadStr()
    {
        return this.Next();
    }
    public char ReadChar()
    {
        return this.Next()[0];
    }
    public int ReadInt()
    {
        return int.Parse(this.Next());
    }
    public uint ReadUInt()
    {
        return uint.Parse(this.Next());
    }
    public long ReadLong()
    {
        return long.Parse(this.Next());
    }
    public double ReadDouble()
    {
        return double.Parse(this.Next());
    }
    public Tuple<int, int> ReadTup(int add = 0)
    {
        return Tuple.Create(this.ReadInt() + add, this.ReadInt() + add);
    }
    public Tuple<long, long> ReadTupLong(int add = 0)
    {
        return Tuple.Create(this.ReadLong() + add, this.ReadLong() + add);
    }
    public Tuple<int, int, int> ReadTup3(int add = 0)
    {
        return Tuple.Create(this.ReadInt() + add, this.ReadInt() + add, this.ReadInt() + add);
    }
    public Tuple<int, int, int, int> ReadTup4(int add = 0)
    {
        return Tuple.Create(this.ReadInt() + add, this.ReadInt() + add, this.ReadInt() + add, this.ReadInt() + add);
    }
    public int[] ReadIntArray(int n)
    {
        var array = new int[n];
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = this.ReadInt();
        }
        return array;
    }
    public long[] ReadLongArray(int n)
    {
        var array = new long[n];
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = this.ReadLong();
        }
        return array;
    }
    public double[] ReadDoubleArray(int n)
    {
        var array = new double[n];
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = this.ReadDouble();
        }
        return array;
    }
    public char[] ReadCharArray(int n)
    {
        var array = new char[n];
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = this.ReadChar();
        }
        return array;
    }
    public string[] ReadStrArray(int n)
    {
        var array = new string[n];
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = this.ReadStr();
        }
        return array;
    }
    public Tuple<long, long>[] ReadTupLongArray(int n, int add = 0)
    {
        var array = new Tuple<long, long>[n];
        for (int i = 0; i < n; i++)
        {
            array[i] = this.ReadTupLong(add);
        }
        return array;
    }
    public Tuple<int, int>[] ReadTupArray(int n, int add = 0)
    {
        var array = new Tuple<int, int>[n];
        for (int i = 0; i < n; i++)
        {
            array[i] = this.ReadTup(add);
        }
        return array;
    }
    public Tuple<int, int, int>[] ReadTup3Array(int n, int add = 0)
    {
        var array = new Tuple<int, int, int>[n];
        for (int i = 0; i < n; i++)
        {
            array[i] = this.ReadTup3(add);
        }
        return array;
    }
    public Tuple<int, int, int, int>[] ReadTup4Array(int n, int add = 0)
    {
        var array = new Tuple<int, int, int, int>[n];
        for (int i = 0; i < n; i++)
        {
            array[i] = this.ReadTup4(add);
        }
        return array;
    }
}

static partial class Algorithm
{
    static int binarySearch<T>(IList<T> a, T v, IComparer<T> cmp, bool islb)
    {
        var l = 0;
        var r = a.Count - 1;
        while (l <= r)
        {
            var m = (l + r) / 2;
            var res = cmp.Compare(a[m], v);
            if (res < 0 || (res == 0 && !islb)) l = m + 1;
            else r = m - 1;
        }
        return l;
    }
    public static int LowerBound<T>(this IList<T> a, T v, Comparison<T> f) { return binarySearch(a, v, Comparer<T>.Create(f), true); }
    public static int LowerBound<T>(this IList<T> a, T v) { return binarySearch(a, v, Comparer<T>.Default, true); }
    public static int UpperBound<T>(this IList<T> a, T v, Comparison<T> cmp) { return binarySearch(a, v, Comparer<T>.Create(cmp), false); }
    public static int UpperBound<T>(this IList<T> a, T v) { return binarySearch(a, v, Comparer<T>.Default, false); }
    public static int ValueCount<T>(this IList<T> a, T v) { return a.UpperBound(v) - a.LowerBound(v); }
}