Problem:
舞踏会 (Ball)

IOI 王国では，王女である JOI 姫の誕生日を祝って舞踏会が開かれることになった．

舞踏会には N 人の貴族が参加する予定である．N は奇数である．貴族には 1 から N までの番号が付けられている．それぞれの貴族には踊りのうまさという整数が定められており，貴族 i (1 ≤ i ≤ N) の踊りのうまさは Di である．

舞踏会では JOI 姫を含む N + 1 人で 2 人ずつ組を作って踊る．IOI 王国では，上級者が初級者を補助できるように，伝統的に以下の方法で踊りの組を決定している．

最初に，N 人の貴族が 1 列に並ぶ．

列に並んでいる貴族が 1 人になるまで，以下の操作を繰り返す．

- 列の先頭から 3 人の貴族の踊りのうまさを調べる．

- その 3 人の貴族の中で，最も踊りのうまさが大きい貴族を A とおく．ただし，複数いる場合は，最も踊りのうまさが大きい貴族の中で，最も番号の小さい貴族 を A とおく．

- その 3 人の貴族の中で，最も踊りのうまさが小さい貴族を B とおく．ただし，複数いる場合は，最も踊りのうまさが小さい貴族の中で，最も番号の大きい貴族 を B とおく．

- A と B が列から抜けて組になる．

- 残った 1 人は列の最後尾に移動する．

最終的に残った 1 人が JOI 姫と組になる．

貴族 1 から貴族 M (1 ≤ M ≤ N − 2) の M 人の貴族については，すでに初期状態で列の何番目に並ぶのかが決まっている．残りの N − M 人の貴族の並び方は国王が自由に決めることができる．

JOI 姫は踊りを学んだばかりなので，国王は JOI 姫と組になる貴族の踊りのうまさをできるだけ大きくしたいと考えている．JOI 姫と組になる貴族の踊りのうまさとして考えられる最大値を求めよ．

課題

それぞれの貴族の踊りのうまさと，M 人の貴族の初期状態で並ぶ場所が与えられたとき，JOI 姫と組になる貴族の踊りのうまさとして考えられる最大値を求めるプログラムを作成せよ．

入力

標準入力から以下のデータを読み込め．

1 行目には，2 個の整数 N, M が空白を区切りとして書かれている．これは舞踏会に貴族が N 人参加し，列に並ぶ場所がすでに決まっている貴族が M 人いることを表す．

続く M 行のうちの i 行目 (1 ≤ i ≤ M) には，2 個の整数 Di, Pi が空白を区切りとして書かれている．これは貴族 i の踊りのうまさが Di で，貴族 i が初期状態で列の先頭から Pi 番目に並ぶことを表す．

続く N − M 行のうちの i 行目 (1 ≤ i ≤ N − M) には，整数 Di+M が書かれている．これは貴族 (i + M)の踊りのうまさが Di+M であることを表す．

出力

標準出力に，JOI 姫と組になる貴族の踊りのうまさとして考えられる最大値を表す整数を 1 行で出力せよ．

制限

すべての入力データは以下の条件を満たす．

3 ≤ N ≤ 99 999．

N は奇数である．

1 ≤ M ≤ N − 2．

1 ≤ Di ≤ 1 000 000 000 (1 ≤ i ≤ N)．

1 ≤ Pi ≤ N (1 ≤ i ≤ M)．

Pi ≠ Pj (1 ≤ i < j ≤ M)．

入出力例

入力例 1

7 3
5 2
5 5
8 6
6
2
8
9

出力例 1

8

初期状態では 3 人の貴族の並ぶ場所がすでに決まっている．

括弧内の数字は踊りのうまさを表す．左端が列の先頭である．

例えば，先頭から順に貴族 5，貴族 1，貴族 4，貴族 6，貴族 2，貴族 3，貴族 7 という順番に並んだ場
合を考える．

すべての貴族が並んだあとの配置

この場合，以下のように列が変化していく．

列の先頭の 3 人の貴族 (貴族 5，貴族 1，貴族 4) 中で，最も踊りのうまさが大きい貴族 4 と最も踊りのうまさが小さい貴族 5 が組になり，残った貴族 1 が最後尾に移動する．

次に，列の先頭の 3 人の貴族 (貴族 6，貴族 2，貴族 3) の中で，最も踊りのうまさが大きい貴族は貴族 6 と貴族 3 の 2 人であり，このうち番号の小さい貴族は貴族 3 である．また，列の先頭の 3 人の貴族のうち最も踊りのうまさが小さい貴族は貴族 2 である．貴族 3 と貴族 2 が組になり，残った貴族 6 が最後尾に移動する．

次に，列の先頭の 3 人の貴族 (貴族 7，貴族 1，貴族 6) の中で，最も踊りのうまさが大きい貴族 7 と最も踊りのうまさが小さい貴族 1 が組になり，残った貴族 6 が最後尾に移動する．

最終的に貴族 6 が残り，JOI 姫と組になる．貴族 6 の踊りのうまさは 8 である．この値が JOI 姫と組になる貴族の踊りのうまさとして考えられる最大値である．

列の変化の様子

入力例 2

3 1
5 3
5
5

出力例 2

5

どのような順番で並んでも，貴族 2 と JOI 姫が組になる．

入力例 3

7 2
32 4
27 6
37
41
41
30
27

出力例 3

37

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in JAVA (judge verdict: Time Limit Exceeded)

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;

public class Main {
	static boolean debug = false;

	public static void main(String[] args) throws IOException {

		UserScanner scan = new UserScanner(System.in);
		PrintWriter pwriter = new PrintWriter(System.out);

		int n = scan.nextInt();
		int m = scan.nextInt();
		Ball ball = new Ball(n, m, debug);

		for (int i = 0; i < m; i++)
			ball.setDancePosition(scan.nextInt(), scan.nextInt());
		for (int i = 0; i < n - m; i++)
			ball.setDance(scan.nextInt());

		pwriter.println(ball.getMaxDance());

		pwriter.flush();

		scan.close();
		System.exit(0);
	}
}

class Ball {
	boolean debug;

	class Gr {
		int[] ch = new int[3]; // if leaf is true, then set dance skill,
								// otherwise child group number
		boolean[] leaf = new boolean[3];
	}

	Gr[] gr;
	int root;

	int[] pool;
	int px = 0;
	int[] allNoble;
	int ax = 0;

	public Ball(int n, int m, boolean debug) {
		this.debug = debug;
		// number of group is N/3 + N/3/3 + N/3/3/3 + ... <= N/2
		gr = new Gr[(n / 2) + 1];
		root = -1;
		int c = 0, p = 0;
		for (int i = 0; root == -1; i++) {
			gr[i] = new Gr();
			for (int j = 0; j < 3; j++)
				if (c < n) {
					gr[i].leaf[j] = true;
					gr[i].ch[j] = -1;
					c++;
				} else if (p == i)
					root = i - 1;
				else {
					gr[i].leaf[j] = false;
					gr[i].ch[j] = p++;
				}
		}

		pool = new int[n - m];
		allNoble = new int[n];
	}

	public int getMaxDance() {
		if (debug)
			printGr(root);

		Arrays.sort(pool); // not allocated nobles
		Arrays.sort(allNoble);
		if (debug)
			for (int i = 0; i < allNoble.length; i++)
				System.out.println("noble " + i + ", d=" + allNoble[i]);

		int hi = 1;
		for (int i = allNoble.length / 3; i > 0; i /= 3)
			hi *= 2; // possible best dancer, if 3 persons then 2nd, if 9
						// persons then 4th, if 27 persons 8th is possible.
						// (it's not accurate)
		hi = allNoble.length - hi;
		int lo = 0, maxDance = -1, midDance = -1;
		boolean down = true;

		while (hi >= lo) { // search max dance skill
			int mid = lo + (hi - lo) / 2;
			if (midDance == allNoble[mid])
				if (down) {
					hi = mid;
					continue;
				} else {
					lo = mid + 1;
					continue;
				}
			midDance = allNoble[mid];
			if (isPossible(midDance, getPoolNoble(midDance))) {
				// if it possible to assign this dance skill
				// check more higher
				maxDance = midDance;
				down = false;
				lo = mid;
			} else {
				down = true;
				hi = mid;
			}
			if (hi == lo)
				break;
		}

		return maxDance;
	}

	private boolean isPossible(int d, int f) {
		if (debug)
			System.out.println("d=" + d + ", f=" + f + ", demand=" + getLeastNoble(root, d));

		// dance skill d, and f is free nobles which have dance skill >= d
		if (getLeastNoble(root, d) <= f)
			return true;
		else
			return false;
	}

	private int getLeastNoble(int p, int d) {
		// count the noble which are required to allocate
		int[] a = new int[3];
		for (int i = 0; i < 3; i++)
			if (gr[p].leaf[i])
				if (gr[p].ch[i] >= d)
					a[i] = 0; // already allocated
				else if (gr[p].ch[i] > 0)
					a[i] = pool.length + 1; // sign for not applicable
				else
					a[i] = 1; // need to allocate
			else
				a[i] = getLeastNoble(gr[p].ch[i], d); // child group check by
														// recursive

		// need 2 nobles (highest skill and middle)
		return Math.min(pool.length + 1, a[0] + a[1] + a[2] - Math.max(a[0], Math.max(a[1], a[2])));

	}

	private int getPoolNoble(int d) {
		// count free nobles which have higher dance skill
		if (pool[0] >= d)
			return pool.length;
		if (pool[pool.length - 1] < d)
			return 0;
		return getPoolN(d, 0, pool.length);
	}

	private int getPoolN(int d, int lo, int hi) {
		if (hi <= lo)
			if (pool[lo] >= d)
				return pool.length - lo;
			else
				return pool.length - lo - 1;
		int mid = lo + (hi - lo) / 2;
		if (pool[mid] >= d)
			return getPoolN(d, lo, mid);
		else
			return getPoolN(d, lo + (hi - lo + 1) / 2, hi);
	}

	public void setDance(int d) {
		pool[px++] = d;
		allNoble[ax++] = d;
	}

	public void setDancePosition(int d, int p) {
		p--;
		gr[p / 3].ch[p % 3] = d;
		allNoble[ax++] = d;
	}

	private void printGr(int p) {
		System.out.println(" Gr=" + p);
		System.out.print("   ");
		if (gr[p].leaf[0])
			System.out.print(gr[p].ch[0]);
		else
			System.out.print("Gr=" + gr[p].ch[0]);
		if (gr[p].leaf[1])
			System.out.print(", " + gr[p].ch[1]);
		else
			System.out.print(", Gr=" + gr[p].ch[1]);
		if (gr[p].leaf[2])
			System.out.print(", " + gr[p].ch[2]);
		else
			System.out.print(", Gr=" + gr[p].ch[2]);
		System.out.println();
		if (!gr[p].leaf[0])
			printGr(gr[p].ch[0]);
		if (!gr[p].leaf[1])
			printGr(gr[p].ch[1]);
		if (!gr[p].leaf[2])
			printGr(gr[p].ch[2]);
	}

}

class UserScanner {
	private InputStream in;
	private final byte[] buffer = new byte[1024];
	private int ptr = 0;
	private int buflen = 0;

	public UserScanner(InputStream inStream) {
		in = inStream;
	}

	private void read() {
		ptr = 0;
		try {
			buflen = in.read(buffer);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(9);
		}
	}

	private byte getByte() {
		if (ptr >= buflen)
			read();
		if (buflen < 0 || isCtlSpace(buffer[ptr])) {
			return -1;
		} else
			return buffer[ptr++];
	}

	private void skipCtlSpace() {
		for (; ptr < buflen; ptr++)
			if (!isCtlSpace(buffer[ptr]))
				return;
		read();
		skipCtlSpace();
	}

	private boolean isCtlSpace(byte b) {
		if (b <= ' ' || b > '~')
			return true;
		else
			return false;
	}

	public void close() {
		try {
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(9);
		}
	}

	public String next() {
		skipCtlSpace();
		StringBuilder sb = new StringBuilder();
		byte b;
		while ((b = getByte()) != -1) {
			sb.appendCodePoint(b);
		}
		return sb.toString();
	}

	public int nextInt() {
		skipCtlSpace();
		int n = 0;
		boolean minus = false;
		byte b;
		while ((b = getByte()) != -1) {
			if (b == '-')
				minus = true;
			else {
				n *= 10;
				n += (b - '0');
			}
		}
		if (minus)
			return n * -1;
		else
			return n;
	}
}