【CZY选讲·棋盘迷宫】
题目描述
一个N*M的棋盘,’.’表示可以通过,’#’表示不能通过,给出Q个询问,给定起点和终点,判断两点是否联通,如联通输出“Yes”,否则输出“No”。
数据范围
N,M <=500,Q <=10^6。
题解:
①由于存在两个方向和不可逆性,标记联通分量的方法不可行
②分治算法。按照行将棋盘一分为二,进行DP处理:
用f[i][j]表示点(i,j)与中线上每个点的联通性,可用bitset压位处理。
转移方程式:f[i][j]=f[i][j+1]|f[i+1][j]
③离线询问,将询问随着分治区间而分开处理。
④时间复杂度: O(n2*logn)
#include <cstdio> #include <vector> #include <bitset> using std::vector; using std::bitset; const int QUERY_SIZE = 600006; const int MAP_SIZE = 511; int N, M, Q; char map[MAP_SIZE][MAP_SIZE]; int ans[QUERY_SIZE]; bitset<MAP_SIZE> f[MAP_SIZE][MAP_SIZE], g[MAP_SIZE][MAP_SIZE]; struct query { int x1, y1, x2, y2, id; }; query q; void solve(vector<query> v, int l, int r) { int m = (l + r) >> 1; if (l > r) return ; for (int i = m; i >= l; i--) for (int j = M; j >= 1; j--) { f[i][j] = 0; if (map[i][j] == '.') { if (i == m) f[i][j].set(j); else f[i][j] |= f[i + 1][j]; if (j != M) f[i][j] |= f[i][j + 1]; } } for (int i = m; i <= r; i++) for (int j = 1; j <= M; j++) { g[i][j] = 0; if (map[i][j] == '.') { if (i == m) g[i][j].set(j); else g[i][j] |= g[i - 1][j]; if (j != 1) g[i][j] |= g[i][j - 1]; } } vector<query> vl, vr; for (vector<query>::iterator it = v.begin(); it != v.end(); it++) { q = *it; if (q.x2 < m) vl.push_back(q); else if (q.x1 > m) vr.push_back(q); else ans[q.id] = (f[q.x1][q.y1] & g[q.x2][q.y2]).any(); } solve(vl, l, m - 1); solve(vr, m + 1, r); } int main() { freopen("boardgame.in", "r", stdin); freopen("boardgame.out", "w", stdout); scanf("%d %d", &N, &M); for (int i = 1; i <= N; i++) scanf("%s", map[i] + 1); vector<query> v; scanf("%d", &Q); for (int i = 0; i < Q; i++) { scanf("%d %d %d %d", &q.x1, &q.y1, &q.x2, &q.y2); q.id = i; v.push_back(q); } solve(v, 1, N); for (int i = 0; i < Q; i++) puts(ans[i] ? "Yes" : "No"); return 0; }//czy020202
那神秘的光芒像暴风雨般凛冽着,大地在无情的追问中幻灭成挽歌,
如梦的迷雾随着诗篇消逝在远山…… ————————————汪峰《信仰在空中飘扬》