Problem p00465 (C++) — buggy submission, verdict: Time Limit Exceeded

// 認証レベルの小さい順に鍵を設定すると、入れる部屋が単調にふえるので、その部屋に入るための最少認証レベルが簡単に求まる。
// と思ったら、そうでもなかった…とりあえず、ダイクストラの負の辺OK盤を使う。
#include <iostream>
#include <map>
#include <queue>
#include <vector>
// #define DEBUG DEBUG
#define F first
#define S second
using namespace std;
#define MAX(x, y) (x > y ? x : y) // ()がいります
typedef pair<int, int> P;
typedef pair<int, P> PP;
int R;
int W1, H1, X1, Y1, W2, H2, X2, Y2;
int JOI1[501][501], JOI2[501][501]; // JOI[y][x]として入れます。
int ADD1[501][501], ADD2[501][501]; // 入るための認証レベルの最小値(1以上)

void Make_list1() {
  priority_queue<PP, vector<PP>, greater<PP>> que;
  PP node;
  que.push(PP(1, P(Y1, X1)));
  while (!que.empty()) {
    node = que.top();
    que.pop();
    // 周り4辺を更新
    if (ADD1[node.S.F + 1][node.S.S] >
        MAX(node.F, JOI1[node.S.F + 1][node.S.S])) {
      ADD1[node.S.F + 1][node.S.S] = MAX(node.F, JOI1[node.S.F + 1][node.S.S]);
      que.push(PP(ADD1[node.S.F + 1][node.S.S], P(node.S.F + 1, node.S.S)));
    }
    if (ADD1[node.S.F][node.S.S + 1] >
        MAX(node.F, JOI1[node.S.F][node.S.S + 1])) {
      ADD1[node.S.F][node.S.S + 1] = MAX(node.F, JOI1[node.S.F][node.S.S + 1]);
      que.push(PP(ADD1[node.S.F][node.S.S + 1], P(node.S.F, node.S.S + 1)));
    }
    if (node.S.F > 0 && ADD1[node.S.F - 1][node.S.S] >
                            MAX(node.F, JOI1[node.S.F - 1][node.S.S])) {
      ADD1[node.S.F - 1][node.S.S] = MAX(node.F, JOI1[node.S.F - 1][node.S.S]);
      que.push(PP(ADD1[node.S.F - 1][node.S.S], P(node.S.F - 1, node.S.S)));
    }
    if (node.S.S > 0 && ADD1[node.S.F][node.S.S - 1] >
                            MAX(node.F, JOI1[node.S.F][node.S.S - 1])) {
      ADD1[node.S.F][node.S.S - 1] = MAX(node.F, JOI1[node.S.F][node.S.S - 1]);
      que.push(PP(ADD1[node.S.F][node.S.S - 1], P(node.S.F, node.S.S - 1)));
    }
  }
}
void Make_list2() {
  priority_queue<PP, vector<PP>, greater<PP>> que;
  PP node;
  que.push(PP(1, P(Y2, X2)));
  while (!que.empty()) {
    node = que.top();
    que.pop();
    // 周り4辺を更新
    if (ADD2[node.S.F + 1][node.S.S] >
        MAX(node.F, JOI2[node.S.F + 1][node.S.S])) {
      ADD2[node.S.F + 1][node.S.S] = MAX(node.F, JOI2[node.S.F + 1][node.S.S]);
      que.push(PP(ADD2[node.S.F + 1][node.S.S], P(node.S.F + 1, node.S.S)));
    }
    if (ADD2[node.S.F][node.S.S + 1] >
        MAX(node.F, JOI2[node.S.F][node.S.S + 1])) {
      ADD2[node.S.F][node.S.S + 1] = MAX(node.F, JOI2[node.S.F][node.S.S + 1]);
      que.push(PP(ADD2[node.S.F][node.S.S + 1], P(node.S.F, node.S.S + 1)));
    }
    if (node.S.F > 0 && ADD2[node.S.F - 1][node.S.S] >
                            MAX(node.F, JOI2[node.S.F - 1][node.S.S])) {
      ADD2[node.S.F - 1][node.S.S] = MAX(node.F, JOI2[node.S.F - 1][node.S.S]);
      que.push(PP(ADD2[node.S.F - 1][node.S.S], P(node.S.F - 1, node.S.S)));
    }
    if (node.S.S > 0 && ADD2[node.S.F][node.S.S - 1] >
                            MAX(node.F, JOI2[node.S.F][node.S.S - 1])) {
      ADD2[node.S.F][node.S.S - 1] = MAX(node.F, JOI2[node.S.F][node.S.S - 1]);
      que.push(PP(ADD2[node.S.F][node.S.S - 1], P(node.S.F, node.S.S - 1)));
    }
  }
}
int main() {
  int i, j;
  int minest[200], mcor = 0;
  for (i = 0; i < 200; i++) {
    minest[i] = 1000000000;
  }
  while (1) {
    cin >> R;
    if (R == 0)
      break;
    cin >> W1 >> H1 >> X1 >> Y1;
    X1--;
    Y1--;
    for (i = 0; i < H1; i++) {
      for (j = 0; j < W1; j++) {
        cin >> JOI1[i][j];
        ADD1[i][j] = 1000000001;
      }
    }
    ADD1[Y1][X1] = 1;
    cin >> W2 >> H2 >> X2 >> Y2;
    X2--;
    Y2--;
    for (i = 0; i < H2; i++) {
      for (j = 0; j < W2; j++) {
        cin >> JOI2[i][j];
        ADD2[i][j] = 1000000001;
      }
    }
    ADD2[Y2][X2] = 1;
    // 幅優先探索で部屋に入るための認証レベルの最小値,R部屋入るための認証レベルの最小値を記録する。
    // 認証レベルの設定はWH回したほうがよい。まちがっても、部屋数に対する認証レベルを探すO(R^2logWH)解法は行わない！
    // queueを使うとO(WH)で行ける。WH*最大4方向*最大4回更新
    Make_list1();
    Make_list2();
    // 認証レベルに対して入れる部屋数を考えてみよう(ヒント→認証レベルがiのとき認証レベルがi-1のとき行けた部屋はすべていける)
    static map<int, int> nin1, nin2; // 認証レベル,部屋数
    nin1.insert(make_pair(0, 0));
    for (i = 0; i < H1; i++) {
      for (j = 0; j < W1; j++) {
        if (nin1.find(ADD1[i][j]) == nin1.end()) {
          nin1.insert(make_pair(ADD1[i][j], 0));
        }
        nin1[ADD1[i][j]]++; // 2番目のキーをプラスしましょう。
      }
    }
    nin2.insert(make_pair(0, 0));
    for (i = 0; i < H2; i++) {
      for (j = 0; j < W2; j++) {
        if (nin2.find(ADD2[i][j]) == nin2.end()) {
          nin2.insert(make_pair(ADD2[i][j], 0));
        }
        nin2[ADD2[i][j]]++; // 2番目のキーをプラスしましょう。
      }
    }
    map<int, int>::iterator it, bit;
    it = nin1.begin();
    for (it++; it != nin1.end(); it++) {
      bit = it;
      bit--;
      (*it).S += (*bit).S;
    }
    it = nin2.begin();
    for (it++; it != nin2.end(); it++) {
      bit = it;
      bit--;
      (*it).S += (*bit).S;
    }
    // 入れる部屋数にたいする認証レベルの最小値を考えてみよう
    static int R1[100001], R2[100001];
    for (i = 0; i < R + 1; i++) {
      R1[i] = 1000000000;
      R2[i] = 1000000000;
    }
    it = nin1.begin();
    R1[0] = 0;
    it++;
    for (; it != nin1.end(); it++) {
      if ((*it).S > R)
        (*it).S = R;
      bit = it;
      bit--;
      for (i = (*it).S; i > (*bit).S; i--) {
        if (R1[i] > (*it).F)
          R1[i] = (*it).F;
      }
    }
    it = nin2.begin();
    R2[0] = 0;
    it++;
    for (; it != nin2.end(); it++) {
      if ((*it).S > R)
        (*it).S = R;
      bit = it;
      bit--;
      for (i = (*it).S; i > (*bit).S; i--) {
        if (R2[i] > (*it).F)
          R2[i] = (*it).F;
      }
    }
    // デバッグ
#ifdef DEBUG
    for (i = 0; i < H1; i++) {
      for (j = 0; j < W1; j++) {
        cout << ADD1[i][j] << " ";
      }
      cout << endl;
    }
    for (i = 0; i < H2; i++) {
      for (j = 0; j < W2; j++) {
        cout << ADD2[i][j] << " ";
      }
      cout << endl;
    }
    cout << "(NL,OK)1部屋目" << endl;
    for (it = nin1.begin(); it != nin1.end(); it++) {
      cout << (*it).F << " " << (*it).S << endl;
    }
    cout << "(NL,OK)2部屋目" << endl;
    for (it = nin2.begin(); it != nin2.end(); it++) {
      cout << (*it).F << " " << (*it).S << endl;
    }
    // 認証レベル
    cout << "NL1部屋目" << endl;
    for (i = 0; i < H1; i++) {
      for (j = 0; j < W1; j++) {
        cout << R1[i * W1 + j] << " ";
      }
      cout << endl;
    }
    cout << R1[H1 * W1] << endl;
    cout << "NL2部屋目" << endl;
    for (i = 0; i < H2; i++) {
      for (j = 0; j < W2; j++) {
        cout << R2[i * W2 + j] << " ";
      }
      cout << endl;
    }
    cout << R2[H1 * W1] << endl;
#endif
    // 探索(O(R))
    for (i = 0; i < R + 1; i++) {
      if (minest[mcor] > R1[i] + R2[R - i])
        minest[mcor] = R1[i] + R2[R - i];
    }
    // 使った分を初期化
    for (i = 0; i < H1; i++) {
      for (j = 0; j < W1; j++) {
        JOI1[i][j] = 0;
        ADD1[i][j] = 0;
      }
    }
    for (i = 0; i < H2; i++) {
      for (j = 0; j < W2; j++) {
        JOI2[i][j] = 0;
        ADD2[i][j] = 0;
      }
    }
    nin1.clear();
    nin2.clear();
    for (i = 0; i < R + 1; i++) {
      R1[i] = 1000000000;
      R2[i] = 1000000000;
    }
    // カーソル移動
    mcor++;
  }
  for (i = 0; i < mcor; i++) {
    cout << minest[i] << endl;
  }
  return 0;
}

Error: TLE

Fixed submission:

// オーダーは大体O(RW1H1logW2H2)くらい
#include <algorithm>
#include <iostream>
using namespace std;

int R;
int W[2], H[2], ex[2], ey[2];
int WS[2][500][500];
int NL[2][250001];
int AD[2][500][500];

void CANGO(int place, int NLINDEX, int x, int y, int *entered) {
  if (WS[place][y][x] > NL[place][NLINDEX]) {
    return;
  }
  if (x >= W[place] || y >= H[place] || x < 0 || y < 0) {
    return;
  }
  if (AD[place][y][x] == 1) {
    return;
  } else {
    AD[place][y][x] = 1;
    (*entered)++;
    CANGO(place, NLINDEX, x + 1, y, entered);
    CANGO(place, NLINDEX, x - 1, y, entered);
    CANGO(place, NLINDEX, x, y + 1, entered);
    CANGO(place, NLINDEX, x, y - 1, entered);
  }
  if (*entered >= R)
    return;
  return;
}
void IniAD(int place) {
  for (int i = 0; i < H[place]; i++) {
    for (int j = 0; j < W[place]; j++) {
      AD[place][i][j] = 0;
    }
  }
}
int main() {
  int i, j, k;
  int ncor[2];
  int mcorsole = 0;
  int minest[100];
  while (1) {
    cin >> R;
    if (R == 0) {
      break;
    }
    ncor[0] = 0;
    ncor[1] = 0;
    for (i = 0; i < 2; i++) {
      cin >> W[i] >> H[i] >> ex[i] >> ey[i];
      ex[i]--;
      ey[i]--;
      for (j = 0; j < H[i]; j++) {
        for (k = 0; k < W[i]; k++) {
          cin >> WS[i][j][k];
          NL[i][ncor[i]] = WS[i][j][k];
          ncor[i]++;
        }
      }
      NL[i][ncor[i]] = 0;
      sort(NL[i], NL[i] + ncor[i] + 1);
    }
    // 探索(1つは2分木)
    int in1, in2;
    int start, last, medi, evergo;
    minest[mcorsole] = 200000001;
    for (i = 0; i < ncor[0] + 1; i++) {
      in1 = 0;
      CANGO(0, i, ex[0], ey[0], &in1);
      IniAD(0);
      evergo = 0;
      start = 0;
      last = ncor[1];
      while (NL[1][start] < NL[1][last]) {
        medi = (start + last) / 2;
        in2 = 0;
        IniAD(1);
        CANGO(1, medi, ex[1], ey[1], &in2);
        if (in1 + in2 >= R) {
          last = medi;
          evergo = 1;
        } else {
          start = medi + 1;
        }
      }
      if (NL[0][i] + NL[1][start] < minest[mcorsole] && evergo == 1) {
        minest[mcorsole] = NL[0][i] + NL[1][start];
      }
    }
    mcorsole++;
  }
  for (i = 0; i < mcorsole; i++) {
    cout << minest[i] << endl;
  }
  return 0;
}