Problem:
勉強会

プログラマー養成校のアカベ高校には、生徒自身で運営するユニークな勉強会があります。プログラマーは新しい技術を常に取り入れることが大切なので、この勉強会を通して自学自習の習慣を身につけることがこの活動のねらいです。

生徒は全部でN人おり、それぞれが入学時のプログラミングコンテストの結果で得られたスコアを持っています。勉強会ではN人の生徒のうち何人かがリーダーになり、各リーダーがそれぞれのグループを運営するとともに、自らの運営するグループに参加します。

リーダー以外の生徒は、自分のスコアよりも低いスコアのリーダーが運営するグループには参加できません。また、0以上のある値rを１つ決め、グループに参加する生徒とリーダーのスコアの差がr以内となるようにしています。つまり、グループのリーダーのスコアがsのとき、自分のスコアがsを超えているか、あるいは s - r 未満ならば、そのグループには参加できません。

あなたは勉強会の実行委員長であり、運営準備のためにシミュレーションを行うことにしました。シミュレーションでは、リーダーが誰もいない状態から始め、以下の操作を何回か繰り返します。

生徒をリーダーに加える。

生徒をリーダーから外す。

要求時点でのリーダーの組み合わせについて、どのグループにも参加できない生徒がx人以下になるような、最小のrを求める。

このようなシミュレーションを行うプログラムを作成してください。

入力

入力は1つのデータセットからなる。入力は以下の形式で与えられる。

N Q
s1
s2
:
sN
QUERY1
QUERY2
:
QUERYQ

1行目に生徒の数N(1 ≤ N ≤ 1000000)、処理要求の数Q(0 ≤ Q ≤ 1000)が与えられる。

続くN行にi番目の生徒のスコアを示す整数si(0 ≤ si ≤ 1,000,000,000)が与えられる。生徒は1,2,...,N で番号付けされているものとする。

続くQ行に処理要求QUERYiが与えられる。処理要求は時系列順に与えられる。処理要求はADD, REMOVE, CHECKの3種類あり、各QUERYiは以下のいずれかの形式で与えられる。

ADD a

または

REMOVE a

または

CHECK x

ADD aは番号a(1 ≤ a ≤ N)の生徒をリーダーに加えることを表す。

REMOVE aは番号a(1 ≤ a ≤ N)の生徒をリーダーから外すことを表す。

CHECK xは出力要求を表す。どのグループにも参加できない生徒の数の上限x(0 ≤ x ≤ N)が与えられる。

なお、入力は以下の条件を満たすものとする。

どの時点でも、リーダーの人数が100人を超えることはない。

その時点でリーダーである生徒をリーダーに加えることはない。

その時点でリーダーでない生徒をリーダーから外すことはない。

出力

時系列順に各出力要求の時点で、どのグループにも参加できない生徒がx人以下になるような最小のrを1行に出力する。ただし、どのようなrを選んでもx人以下にすることが不可能であればNAと出力する。

入力例 1

5 8
5
10
8
7
3
ADD 1
ADD 3
CHECK 0
CHECK 1
CHECK 2
CHECK 3
CHECK 4
CHECK 5

出力例 1

NA
2
1
0
0
0

入力例 2

5 28
5
10
8
7
3
CHECK 0
CHECK 1
CHECK 2
CHECK 3
CHECK 4
CHECK 5
ADD 1
CHECK 0
CHECK 1
CHECK 2
CHECK 3
CHECK 4
CHECK 5
REMOVE 1
ADD 3
CHECK 0
CHECK 1
CHECK 2
CHECK 3
CHECK 4
CHECK 5
ADD 1
CHECK 0
CHECK 1
CHECK 2
CHECK 3
CHECK 4
CHECK 5

出力例 2

NA
NA
NA
NA
NA
0
NA
NA
NA
2
0
0
NA
5
3
1
0
0
NA
2
1
0
0
0

Solution in C (judge verdict: Accepted)

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define MAX_N (int)1e6
const int INF = (int)1e9 + 1;

typedef struct {
    int f, s;
} Pair;

int PairCmp( const void* a, const void* b ) { return ((Pair*)a)->f - ((Pair*)b)->f; }

int n, q;
Pair tmp[MAX_N];
int s[MAX_N];//下からi番目のスコア: s[i]
int f[MAX_N];//i番の人がスコア何番目か: f[i]

int m = 0;
int leader[MAX_N];//リーダーの番号をスコア順で記録

void Insert( int v ) {
    int i = 0, j;
    for ( ; i<m; ++i ) {
	if ( leader[i] > v ) break;
    }
    for ( j=m; j>i; --j ) {
	leader[j] = leader[j-1];
    }
    leader[i] = v;
    ++m;
}

void Erase( int v ) {
    int i = 0, j;
    for ( ; i<m; ++i ) {
	if ( leader[i] == v ) break;
    }
    for ( j=i+1; j<m; ++j ) {
	leader[j-1] = leader[j];
    }
    --m;
}

int LowerBound( int v ) {
    int l = 0, r = n;
    while ( r - l > 1 ) {
	int mid = ( l + r )/2;
	if ( s[mid] >= v ) r = mid;
	else l = mid;
    }
    return v <= s[l] ? l : r;
}

int UpperBound( int v ) { return LowerBound(v+1); }

int main() {

    int i;
    char op[10];
    int arg;

    scanf("%d %d", &n, &q);

    for ( i=0; i<n; ++i ) {
	scanf("%d", &tmp[i].f);
	tmp[i].s = i;
    }
    qsort( tmp, n, sizeof(Pair), PairCmp );

    for ( i=0; i<n; ++i ) {
	s[i] = tmp[i].f;
	f[tmp[i].s] = i;
    }

    while ( q-- ) {

	scanf("%s %d", op, &arg);

        if      ( op[0] == 'A' ) Insert( f[arg-1] );
        else if ( op[0] == 'R' ) Erase( f[arg-1] );
        else {//CHECK

            int fail_r = -1, succ_r = INF;//条件を満たさない最大のr と 満たす最小のr

            while ( succ_r - fail_r > 1 ) {
		
                int mid = (succ_r + fail_r)/2;//新しいr

                int count = 0; //リーダーによってカバーされる生徒の総数
		int prev = -1; //リーダーのうちスコアが一つ下の人のインデックス
                for ( int i=0; i<m; ++i ) {

		    int idx = leader[i];

		    //全スコア中でリーダーからr引いたスコアの人のインデクス
                    int l_idx = LowerBound( s[idx] - mid );

		    //全スコア中でリーダーのスコアのすぐ下の人のインデクス
                    int r_idx = UpperBound( s[idx] ) - 1;

                    if ( l_idx <= prev ) l_idx = prev + 1;//一つ下のリーダーの手前まで

                    count += r_idx - l_idx + 1;//カウントにリーダー自身を含める
		    
                    prev = r_idx;
                }

                if ( n - count <= arg ) succ_r = mid;
                else fail_r = mid;
            }

	    if ( succ_r == INF ) printf("NA\n");
	    else printf("%d\n", succ_r);
        }
    }
    
    return 0;
}