Problem:
モジュロ・クエリ

あなたに N 枚のカードを渡します。どのカードにも一つだけ自然数が書いてあります。ただし、同じ数が書いてあることはありません。

これから質問として、適当な自然数を言います。あなたが持っているカードに書いてある数を私が言った数で割ったときに得られる余りのうち最も大きなものを答えてください。

たとえば、あなたは 3 枚のカードを持っていて、それぞれ 9、3、8 と書いてあるとします。私が「4」と言ったら、9 と 3 と 8 をそれぞれ4 で割った余りを求めてください。余りはそれぞれ 1、3、0 ですが、この中でもっとも大きな余りは3 なので、3 が正しい答えになります。

では始めましょうか。え？ カードがいっぱいあるとたいへんだ？ しょうがないですね。それではコ
ンピュータを使って最大の余りを見つけることにしましょう。カードに書いてある数を、質問された数で割った余りのうち、最大のものを見つけるプログラムを作成してください。なお、質問は１回だけでなく何度もしますが、同じ数を 2 回以上質問することはありません。

入力

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

N Q
c1 c2 ... cN
q1
q2
:
qQ

１行目にカードの枚数 N (2 ≤ N ≤ 300000) と質問の回数 Q (2 ≤ Q ≤ 100000) が１つの空白区切りで与えられ、２行目にカードに書かれた数 ci (1 ≤ ci ≤ 300000) が１つの空白区切りで与えられる。続くQ 行に質問として与えられる数 qi (1 ≤ qi ≤ 300000) が与えられる。

出力

質問ごとに最大の余りを１行に出力する。

入力例

3 3
9 3 8
4
6
5

出力例

3
3
4

Solution in C++ (judge verdict: Accepted)

#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>

using namespace std;

int main(){

	int* checkTable = new int[300001];
	int* ansTable = new int[300002];
	int N,Q,tmp;

	for(int i = 1; i <= 300000; i++){
		checkTable[i] = -1;
		ansTable[i] = -1;
	}

	scanf("%d %d",&N,&Q);

	for(int i = 0; i < N; i++){
		scanf("%d",&tmp);
		checkTable[tmp] = tmp;
		ansTable[tmp+1] = tmp;
	}

	int tmp_max;

	for(int i = 1; i <= 300000; i++){
		if(checkTable[i] == -1){
			checkTable[i] = checkTable[i-1];
		}
	}

	ansTable[1] = 0;
	for(int i = 2; i <= 300000;i++){
		if(ansTable[i] == -1){
			tmp_max = 0;
			for(int k = i-1; k <= 300000; k += i){
				tmp_max = max(tmp_max,checkTable[k]%i);
				if(tmp_max == i-1)break;
			}
			ansTable[i] = tmp_max;
		}
	}

	int q;
	for(int i = 0; i < Q; i++){
		scanf("%d",&q);

		printf("%d\n",ansTable[q]);
	}
	return 0;
}