Problem:
Score : 500 points

Problem Statement

Takahashi has come to a party as a special guest.
There are N ordinary guests at the party. The i-th ordinary guest has a power of A_i.

Takahashi has decided to perform M handshakes to increase the happiness of the party (let the current happiness be 0).
A handshake will be performed as follows:

Takahashi chooses one (ordinary) guest x for his left hand and another guest y for his right hand (x and y can be the same).

Then, he shakes the left hand of Guest x and the right hand of Guest y simultaneously to increase the happiness by A_x+A_y.

However, Takahashi should not perform the same handshake more than once. Formally, the following condition must hold:

Assume that, in the k-th handshake, Takahashi shakes the left hand of Guest x_k and the right hand of Guest y_k. Then, there is no pair p, q (1 \leq p < q \leq M) such that (x_p,y_p)=(x_q,y_q).

What is the maximum possible happiness after M handshakes?

Constraints

1 \leq N \leq 10^5

1 \leq M \leq N^2

1 \leq A_i \leq 10^5

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N M
A_1 A_2 ... A_N

Output

Print the maximum possible happiness after M handshakes.

Sample Input 1

5 3
10 14 19 34 33

Sample Output 1

202

Let us say that Takahashi performs the following handshakes:

In the first handshake, Takahashi shakes the left hand of Guest 4 and the right hand of Guest 4.

In the second handshake, Takahashi shakes the left hand of Guest 4 and the right hand of Guest 5.

In the third handshake, Takahashi shakes the left hand of Guest 5 and the right hand of Guest 4.

Then, we will have the happiness of (34+34)+(34+33)+(33+34)=202.

We cannot achieve the happiness of 203 or greater, so the answer is 202.

Sample Input 2

9 14
1 3 5 110 24 21 34 5 3

Sample Output 2

1837

Sample Input 3

9 73
67597 52981 5828 66249 75177 64141 40773 79105 16076

Sample Output 3

8128170

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Runtime Error)


import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.IntStream;

public class Main {

    public static void main(String[] args) throws IOException {
        new Main().solve();
    }

    private void solve() throws IOException {
        try {
            //			solveA();
            //			solveB();
            //			solveC();
//			solveD();
            solveE();
            //			 solveF();
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.flush();
                out.close();
            }
        }

    }

    private void solveA() {
        String s = next();
        String t = next();
        out.println(t + s);
    }

    private void solveB() {
        long a = nextLong();
        long b = nextLong();
        long k = nextLong();

        if (k <= a) {
            out.println(a - k + " " + b);
        } else if (k <= a + b) {
            out.println(0 + " " + (b - (k - a)));
        } else {
            out.println(0 + " " + 0);
        }

    }

    private void solveC() {
        int x = nextInt();
        //2は素数
        if (x == 2) {
            out.println(2);
            return;
        }
        //２ではない偶数は素数になれないので奇数へ
        if (x % 2 == 0) {
            x++;
        }
        while (true) {
            boolean sosu = true;
            //奇数しか見ていないので偶数で割れるかの判定は不要
            for (int i = 3; i < x; i += 2) {
                if (x % i == 0) {
                    sosu = false;
                    break;
                }
            }
            if (sosu) {
                break;
            }
            //この時点では、素数の可能性は奇数のみ（２はすでに除外）
            x += 2;
        }
        out.println(x);
    }

    private void solveD() {
        int n = nextInt();
        int k = nextInt();
        int r = nextInt();
        int s = nextInt();
        int p = nextInt();
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        map.put('r', r);
        map.put('s', s);
        map.put('p', p);

        char[] t = next().toCharArray();
        char[] hand = new char[n];
        long res = 0;

        for (int i = 0; i < t.length; i++) {
            char tmp = win(t[i]);
            if (i - k < 0) {
                res += map.get(tmp);
            } else {
                if (hand[i - k] != tmp) {
                    res += map.get(tmp);
                } else {
                    if (i + k < t.length) {
                        tmp = canSelect(hand[i - k], win(t[i + k]), map);
                    } else {
                        tmp = ' ';
                    }
                }
            }
            hand[i] = tmp;
        }

        out.println(res);
    }

    private char win(char aite) {
        switch (aite) {
            case 'r':
                return 'p';
            case 's':
                return 'r';
            case 'p':
                return 's';
        }
        return ' ';
    }

    private char canSelect(char pre, char next, Map<Character, Integer> map) {
        for (Entry<Character, Integer> e : map.entrySet()) {
            if (e.getKey() != pre && e.getKey() != next)
                return e.getKey();
        }
        return ' ';
    }

    /**
     *
     */
    private void solveE() {
        int n = nextInt();
        int m = nextInt();
        int[] wk = IntStream.range(0, n).map(i -> nextInt()).toArray();

        Arrays.sort(wk);
        int left = -1;
        int right = wk[wk.length - 1] + wk[wk.length - 1] + 1;
        while (right - left > 1) {
            int mid = left + (right - left) / 2;
            boolean c = chkHandShake(wk, mid, n, m);
            if (c) {
                right = mid;
            } else {
                left = mid;
            }
        }

        int target = left;
        long[] rui = new long[n + 1];
        for (int i = 0; i < n; i++) {
            rui[i + 1] = rui[i] + wk[i];
        }

        long res = 0;
        /*
        大きいほうから確定していく
         */
        for (int i = n - 1; i >= 0; i--) {
        	/*
        	targetからwk[i]を引いた残りがどこのindexにいるのかの確認
        	 */
            int pos = lowerBound(wk, target - wk[i]);
            long cnt = n - pos;//累積和のためのポジション計算
            res += cnt * wk[i] + (rui[n] - rui[pos]);//累積
            m -= cnt;//mの残数を確認
        }
        /*
        mが-まで行く可能性がある（重複選択）ため、-分targetを引いておく
         */
        res += m * target;
        out.println(res);
    }

    private boolean chkHandShake(int[] nums, int val, int n, int m) {
        long cnt = 0;
        for (int i = 0; i < n; i++) {
            long pos = lowerBound(nums, val - nums[i]);
            cnt += (n - pos);
        }
        return cnt < m;//X以上の和がM個未満かどうか
    }

    private int lowerBound(int[] nums, int val) {
        int left = -1;
        int right = nums.length;
        while (right - left > 1) {
            int mid = left + (right - left) / 2;
            if (nums[mid] >= val) {
                right = mid;
            } else {
                left = mid;
            }
        }
        return right;
    }


    private void solveF() {
        int n = nextInt();

        out.println("");
    }

    /*
     * 階乗計算の最大値
     * もっと大きいのが必要ならここを修正
     */
    private final int FACTORIAL_NUM = 111111;
    /*
     * 階乗の事前計算（mod 済）
     */
    private long[] factorial = new long[FACTORIAL_NUM];
    /*
     * 逆数の事前計算（mod 済）
     */
    private long[] reverseFactorial = new long[FACTORIAL_NUM];
    private boolean isInitFactorial = false;

    /**
     * 階乗計算の事前累積和
     * [1, 1, 2, 3, 4, 5, … FACTORIAL_NUM]
     * mod済
     */
    private void initFactorial(long mod) {
        factorial[0] = 1;
        for (int i = 1; i < FACTORIAL_NUM; i++) {
            factorial[i] = (factorial[i - 1] * i) % mod;
        }
    }

    /**
     * 逆数の事前計算
     * もっと速い方法があったので修正
     */
    private void initReverseFactorial(long mod) {

        reverseFactorial[FACTORIAL_NUM - 1] = modInverse(factorial[FACTORIAL_NUM - 1], mod);
        for (int i = FACTORIAL_NUM - 2; i >= 0; i++) {
            /*
             * 以下を利用する。
             * これにより毎回modInverseしなくてよくなる
             * (N!)^(-1) * N -> ((N - 1)!)^(-1)
             * (N-1!)^(-1) * N -> ((N - 2)!)^(-1)
             */
            reverseFactorial[i] = (reverseFactorial[i + 1] * (i + 1)) % mod;
        }
        //		reverseFactorial[0] = 1;
        //		for (int i = 0; i < FACTORIAL_NUM; i++) {
        //			reverseFactorial[i] = modInverse(factorial[i], mod);
        //		}
    }

    /**
     * フェルマーの小定理を用いて逆元を求める。
     * ある数xのmod p（pは素数）の逆数x'はx' = x^(p-2)
     * 繰り返し二乗法を用いて計算する。
     * http://satanic0258.hatenablog.com/entry/2016/04/29/004730
     * {@link BigInteger#modInverse(BigInteger)}とどちらが速いか？
     *
     * @param x
     * @return
     */
    private long modInverse(long x, long mod) {
        long res = 1L;
        long k = mod - 2L;
        long y = x;
        while (k != 0) {
            if (k % 2 != 0) {
                res = (res * y) % mod;
            }
            y = (y * y) % mod;
            k /= 2;
        }
        return res;
    }

    private void initFactorialCalc(long mod) {
        if (isInitFactorial) {
            //初期化済みならreturn
            return;
        }
        //階乗の事前計算
        initFactorial(mod);
        //逆数の計算もしておく
        initReverseFactorial(mod);
        isInitFactorial = true;
    }

    /**
     * 組み合わせ
     * nCk -> n! / k!・(n-k)!
     *
     * @param n
     * @param k
     * @param mod
     * @return
     */
    private long combination(int n, int k, long mod) {
        initFactorialCalc(mod);
        if (k < 0 || n < k) {
            return 0;
        }

        //分子の計算
        long calResN = factorial[n];

        //分母の計算
        long calResNK = reverseFactorial[n - k];
        long calcResK = reverseFactorial[k];
        long denominator = (calResNK * calcResK) % mod;

        return (calResN * denominator) % mod;

    }

    /**
     * 順列
     * nPk -> n! / (n-k)!
     *
     * @param n
     * @param k
     * @return
     */
    private long permutation(int n, int k, long mod) {
        initFactorialCalc(mod);
        if (k < 0 || n < k) {
            return 0;
        }
        long calResN = factorial[n];
        long calResNK = reverseFactorial[n - k];
        return (calResN * calResNK) % mod;
    }

    /**
     * 多項係数
     * 文字aをp個,bをq個,cをr個, dをs個 あわせてn個を1列に並べるときの並べ方
     * n! / p!・q!・r!・s!
     *
     * @param n
     * @param strNum
     * @param mod
     * @return
     */
    private long multinominalCoefficient(int n, List<Integer> strNum, int mod) {
        initFactorialCalc(mod);

        long calResN = factorial[n];
        long calResDenomi = 1;
        for (Integer num : strNum) {
            calResDenomi *= reverseFactorial[num];
        }
        return (calResN * calResDenomi) % mod;
    }

    /**
     * 重複組合せ nHr （同次積）
     * nHr = (n+r-1)Cr
     * 異なるn個のものから重複を許してr個取る組合せの総数
     * 例：
     * リンゴ，ミカン，牛肉の3種類の果物があります．これらの中から6個の食材を買って帰ります．
     * このとき，何通りの買い方がありますか？ただし，含まれない食材があってもよいものとします
     *
     * @param n
     * @param r
     * @param mod
     * @return
     */
    private long homogeneousProduct(int n, int r, long mod) {
        if (n == 0 && r == 0) {
            return 1;
        }
        return combination(n + r - 1, r, mod);
    }

    private final PrintWriter out = new PrintWriter(System.out);
    private final InputStream in = System.in;
    private final byte[] buffer = new byte[1024];
    private int ptr = 0;
    private int buflen = 0;

    private boolean hasNextByte() {
        if (ptr < buflen) {
            return true;
        } else {
            ptr = 0;
            try {
                buflen = in.read(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (buflen <= 0) {
                return false;
            }
        }
        return true;
    }

    private int readByte() {
        if (hasNextByte())
            return buffer[ptr++];
        else
            return -1;
    }

    private static boolean isPrintableChar(int c) {
        return 33 <= c && c <= 126;
    }

    private void skipUnprintable() {
        while (hasNextByte() && !isPrintableChar(buffer[ptr]))
            ptr++;
    }

    public boolean hasNext() {
        skipUnprintable();
        return hasNextByte();
    }

    public int nextInt() {
        return Integer.parseInt(next());
    }

    public String next() {
        if (!hasNext())
            throw new NoSuchElementException();
        StringBuilder sb = new StringBuilder();
        int b = readByte();
        while (isPrintableChar(b)) {
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }

    public long nextLong() {
        if (!hasNext())
            throw new NoSuchElementException();
        long n = 0;
        boolean minus = false;
        int b = readByte();
        if (b == '-') {
            minus = true;
            b = readByte();
        }
        if (b < '0' || '9' < b) {
            throw new NumberFormatException();
        }
        while (true) {
            if ('0' <= b && b <= '9') {
                n *= 10;
                n += b - '0';
            } else if (b == -1 || !isPrintableChar(b)) {
                return minus ? -n : n;
            } else {
                throw new NumberFormatException();
            }
            b = readByte();
        }
    }
}