1. 程式人生 > 其它 >「SOL」Quick Tortoise (Codeforces)

「SOL」Quick Tortoise (Codeforces)

只能說沒想到


題面

給出一個 \(n\times m\) 的網格圖,每個格子要麼是空地要麼是障礙。

給出 \(q\) 個詢問,每次給出 \((sx, sy),(ex,ey)\),問從 \((sx,sy)\) 出發,只能向下或向右走,能否到達 \((ex,ey)\)

資料規模:\(n,m\le500\)\(q\le6\times10^5\)


解析

只能向右或向下走就保證了轉移無環,由此聯想到另一道題(忘了具體哪道題了):用線段樹維護從 \((l,i)\)\((r,j)\) 的路徑資訊。該題主要利用了兩條路徑比較容易合併的性質。

這道題不涉及修改,不需要線段樹,可以直接貓樹分治

將詢問離線,每次處理橫座標跨過區間中點的詢問。對區間 \([l,r]\)

只需要預處理 \((p,i)\to(mid,k)\)\(p\in[l,mid]\))的連通性和 \((mid,k)\to(q,j)\)\(q\in[mid,r]\))的連通性即可。

詢問只需要判斷是否存在 \(k\) 使得路徑 \((sx,sy)\to(mid,k)\to(ex,ey)\) 合法。

可以用 bitset 優化 —— 儲存 \((p,i)\) 向右下走能到達 \(x=mid\) 的哪些點,以及 \((q,j)\) 向左上走能到達 \(x=mid\) 的哪些點。查詢可以一次 bitset 求交集。

複雜度 \(\mathcal{O}(\frac{n^3\log n+nq}{w})\)


原始碼

/* Lucky_Glass */
#include <bitset>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <algorithm>

#define CON(typ) const typ &

const int N = 505, M = 6e5 + 10;

int rin(int &r) {
  int c = getchar(); r = 0;
  while (c < '0' || '9' < c) c = getchar();
  while ('0' <= c && c <= '9') r = r * 10 + (c ^ '0'), c = getchar();
  return r;
}

int n, m, qry_cnt;
char maz[N][N];
int qry[M][4], qry_id[M], tmp_id[M];
bool ans[M];
std::bitset<N> dp[N][N], dp_rev[N][N];

void solve(CON(int) xl, CON(int) xr, CON(int) ql, CON(int) qr) {
  if (ql > qr) return;
  assert(xl <= xr);
  int mi = (xl + xr) >> 1;

  for (int j = m; j; --j)
    if (maz[mi][j] == '.') dp[mi][j] = dp[mi][j + 1], dp[mi][j].set(j, 1);
    else dp[mi][j] = 0;
  for (int i = mi - 1; i >= xl; --i)
    for (int j = m; j; --j)
      if (maz[i][j] == '.') dp[i][j] = dp[i + 1][j] | dp[i][j + 1];
      else dp[i][j] = 0;
  
  for (int j = 1; j <= m; ++j)
    if (maz[mi][j] == '.') {
      dp_rev[mi][j] = dp_rev[mi][j - 1], dp_rev[mi][j].set(j, 1);
    } else {
      dp_rev[mi][j] = 0;
    }
  for (int i = mi + 1; i <= xr; ++i)
    for (int j = 1; j <= m; ++j)
      if (maz[i][j] == '.') dp_rev[i][j] = dp_rev[i - 1][j] | dp_rev[i][j - 1];
      else dp_rev[i][j] = 0;
  
  int qll = ql - 1, qrr = qr + 1;
  for (int i = ql; i <= qr; ++i)
    if (qry[qry_id[i]][0] <= mi && mi <= qry[qry_id[i]][2]) {
      int *now = qry[qry_id[i]];
      ans[qry_id[i]] = (dp[now[0]][now[1]] & dp_rev[now[2]][now[3]]) != 0;
    } else if (qry[qry_id[i]][0] > mi) {
      tmp_id[--qrr] = qry_id[i];
    } else {
      tmp_id[++qll] = qry_id[i];
    }
  for (int i = ql; i <= qll; ++i) qry_id[i] = tmp_id[i];
  for (int i = qrr; i <= qr; ++i) qry_id[i] = tmp_id[i];

  solve(xl, mi - 1, ql, qll);
  solve(mi + 1, xr, qrr, qr);
}
int main() {
  rin(n), rin(m);
  for (int i = 1; i <= n; ++i) scanf("%s", maz[i] + 1);
  rin(qry_cnt);
  for (int i = 1; i <= qry_cnt; ++i) {
    rin(qry[i][0]), rin(qry[i][1]), rin(qry[i][2]), rin(qry[i][3]);
    qry_id[i] = i;
  }

  solve(1, n, 1, qry_cnt);

  for (int i = 1; i <= qry_cnt; ++i)
    printf("%s\n", ans[i] ? "Yes" : "No");
  return 0;
}

THE END

Thanks for reading!

歷過滄海的變遷
再難為江河的渦旋
我尋找著 沉默的桑田 坎坷萬千
眺望過巫山之巔
再不是浮雲的翩躚
我尋找著 屬於我的那片雲煙
在下個時刻出現

——《巫山雲》By Snapmod / 詩岸

Link 巫山雲 - 網易雲