解法
隣り合っている建物について辺を張って,最小全域木作って,Queryに早く答える。BFSすれば,隣り合っている建物がわかって嬉しい(経路中に建物をまたぐ移動は嬉しくないのは自明)
これでO(HW)。
それで,辺のコストが小さい順につないでいけば,最短距離がわかって(集合と集合がつながったタイミングのコスト)さらに嬉しい。俗に言う最小全域木。
この際に,俗に言うデータ構造をマージする一般的なテクを使ったら部分点70が生えた。
よくよく考えると、O(HW + HW log HW + (P log^2 Q)) ≒ O(2*10^8)くらい(適当)。
間に合ったりしても良さそうだけど,定数や入出力が重かったりしてわりとつらい。(←嘘ついた、マージしても間に合う。(下のソース)
さらに高速化を考えなければいけないんだけど、そのときにLCAを使うとよい。
最大値もダブリングして一緒に計算すれば,1つのクエリーに対数時間で答えることが出来て、 O(HW+HWlogHW+(P + Q)logP) ≒ O(10^8)になって,100点が生える。
ソース
#include<bits/stdc++.h> using namespace std; struct edge { int u, v, cost; bool operator<(const edge& e) const { return cost < e.cost; } bool operator==(const edge& e) const { return(u == e.u && v == e.v && cost == e.cost); } }; struct edge2 { int to, cost; }; typedef pair< int, int > Pi; typedef pair< int, Pi > Pii; typedef vector< edge > Edges; typedef vector< vector< edge2 > > Graph; const int INF = 1 << 30; const int LOG_MAX = 20; struct UnionFind { vector< int > data; UnionFind(int sz) { data.assign( sz, -1); } int Union(int a, int b) { a = Find(a), b = Find(b); if(data[a] < data[b]) swap( a, b); data[b] += data[a]; data[a] = b; return(b); } int Find(int a) { if(data[a] < 0) return(a); else return(data[a] = Find(data[a])); } }; int H, W, P, Q; int A[200000], B[200000], S[200000], T[200000]; bool mas[2000][2000]; Pi Number[2000][2000]; Edges edges; Graph graph; int Depth[200000]; int up[LOG_MAX][200000]; int upc[LOG_MAX][200000]; void bfs() { queue< Pi > que; static const int dy[] = { 0, 1, 0, -1}, dx[] = { 1, 0, -1, 0}; fill_n( *Number, 2000 * 2000, make_pair(-1, -1)); for(int i = 0; i < P; i++) { que.push(make_pair(B[i], A[i])); Number[A[i]][B[i]] = make_pair(0, i); } while(!que.empty()){ Pi p = que.front(); que.pop(); for(int i = 0; i < 4; i++) { int nx = p.first + dx[i], ny = p.second + dy[i]; if(nx < 0 || ny < 0 || nx >= W || ny >= H || mas[ny][nx]) continue; if(Number[ny][nx] != make_pair(-1, -1)) continue; Number[ny][nx] = Number[p.second][p.first]; Number[ny][nx].first++; que.push(make_pair(nx, ny)); } } return; } void make_graph() { for(int i = 0; i < H; i++) { for(int j = 0; j + 1 < W; j++) { int u = Number[i][j].second, v = Number[i][j + 1].second; if(u < 0 || v < 0 || u == v) continue; int cost = Number[i][j].first + Number[i][j + 1].first; edges.push_back((edge){u, v, cost}); } } for(int i = 0; i + 1 < H; i++) { for(int j = 0; j < W; j++) { int u = Number[i][j].second, v = Number[i + 1][j].second; if(u < 0 || v < 0 || u == v) continue; int cost = Number[i][j].first + Number[i + 1][j].first; edges.push_back((edge){u, v, cost}); } } return; } void make_MST() { graph.resize(P); UnionFind uf(P); sort( edges.begin(), edges.end()); edges.erase(unique(edges.begin(), edges.end()), edges.end()); for(int i = 0; i < edges.size(); i++){ edge& e = edges[i]; if(uf.Find(e.u) == uf.Find(e.v)) continue; uf.Union( e.u, e.v); graph[e.u].push_back((edge2){e.v, e.cost}); graph[e.v].push_back((edge2){e.u, e.cost}); } for(int i = 1; i < P; i++) { if(uf.Find(i - 1) == uf.Find(i)) continue; graph[i - 1].push_back((edge2){i, INF}); graph[i].push_back((edge2){i - 1, INF}); uf.Union(i - 1, i); } } int dfs(int idx, int prev, int cost) { if(prev == -1) { for(int i = 0; i < LOG_MAX; i++) { up[i][idx] = -1; } Depth[idx] = 0; } else { up[0][idx] = prev, upc[0][idx] = cost; Depth[idx] = Depth[prev] + 1; for(int i = 1; i < LOG_MAX; i++) { if(up[i - 1][idx] < 0) { up[i][idx] = -1; } else { up[i][idx] = up[i - 1][up[i - 1][idx]]; upc[i][idx] = max( upc[i - 1][idx], upc[i - 1][up[i - 1][idx]]); } } } for(int i = 0; i < graph[idx].size(); i++) { edge2& e = graph[idx][i]; if(e.to != prev) dfs( e.to, idx, e.cost); } } void make_LCA() { dfs( 0, -1, 0); } int queryLCA(int a, int b) { if(Depth[a] > Depth[b]) swap(a, b); int ret = 0; for(int k = LOG_MAX - 1; k >= 0; --k) { if((Depth[b] - Depth[a]) >> k & 1){ ret = max(ret, upc[k][b]); b = up[k][b]; } } if(a == b) return(ret); for(int k = LOG_MAX - 1; k >= 0; --k) { if(up[k][a] != up[k][b]) { ret = max( ret, max( upc[k][a], upc[k][b])); a = up[k][a]; b = up[k][b]; } } return(max(ret, max(upc[0][a], upc[0][b]))); } int main() { scanf("%d %d %d %d", &H, &W, &P, &Q); getchar(); for(int i = 0; i < H; i++) { for(int j = 0; j < W; j++) { char c; scanf("%c", &c); mas[i][j] = (c == '#'); } getchar(); } for(int i = 0; i < P; i++) { scanf("%d %d", &A[i], &B[i]); --A[i], --B[i]; } for(int i = 0; i < Q; i++) { scanf("%d %d", &S[i], &T[i]); --S[i], --T[i]; } bfs(); make_graph(); make_MST(); make_LCA(); int ret; for(int i = 0; i < Q; i++){ printf("%d\n", (ret = queryLCA(S[i], T[i])) < INF ? ret : -1); } }
#include<bits/stdc++.h> using namespace std; struct edge { int u, v, cost; bool operator<(const edge& e) const { return cost < e.cost; } bool operator==(const edge& e) { return(u == e.u && v == e.v && cost == e.cost); } }; typedef pair< int, int > Pi; typedef pair< int, Pi > Pii; typedef vector< edge > Edges; const int INF = 1 << 30; int H, W, P, Q; int A[200000], B[200000], S[200000], T[200000]; bool mas[2000][2000]; Pi Number[2000][2000]; set< int > data[200000]; Edges edges; int answer[200000]; struct UnionFind { vector< int > dat; UnionFind(int sz) { dat.assign( sz, -1); } int Union(int a, int b) { a = Find(a), b = Find(b); if(data[a].size() > data[b].size()) swap(a, b); dat[b] += dat[a]; dat[a] = b; return(b); } int Find(int a) { if(dat[a] < 0) return(a); else return(dat[a] = Find(dat[a])); } }; void bfs() { queue< Pi > que; static const int dy[] = { 0, 1, 0, -1}, dx[] = { 1, 0, -1, 0}; fill_n( *Number, 2000 * 2000, make_pair(-1, -1)); for(int i = 0; i < P; i++) { que.push(make_pair(B[i], A[i])); Number[A[i]][B[i]] = make_pair(0, i); } while(!que.empty()){ Pi p = que.front(); que.pop(); for(int i = 0; i < 4; i++) { int nx = p.first + dx[i], ny = p.second + dy[i]; if(nx < 0 || ny < 0 || nx >= W || ny >= H || mas[ny][nx]) continue; if(Number[ny][nx] != make_pair(-1, -1)) continue; Number[ny][nx] = Number[p.second][p.first]; Number[ny][nx].first++; que.push(make_pair(nx, ny)); } } return; } void make_graph() { for(int i = 0; i < H; i++) { for(int j = 0; j + 1 < W; j++) { int u = Number[i][j].second, v = Number[i][j + 1].second; if(u < 0 || v < 0 || u == v) continue; int cost = Number[i][j].first + Number[i][j + 1].first; edges.push_back((edge){u, v, cost}); } } for(int i = 0; i + 1 < H; i++) { for(int j = 0; j < W; j++) { int u = Number[i][j].second, v = Number[i + 1][j].second; if(u < 0 || v < 0 || u == v) continue; int cost = Number[i][j].first + Number[i + 1][j].first; edges.push_back((edge){u, v, cost}); } } return; } int main() { scanf("%d %d %d %d", &H, &W, &P, &Q); getchar(); for(int i = 0; i < H; i++) { for(int j = 0; j < W; j++) { char c; scanf("%c", &c); mas[i][j] = (c == '#'); } getchar(); } for(int i = 0; i < P; i++) { scanf("%d %d", &A[i], &B[i]); --A[i], --B[i]; } for(int i = 0; i < Q; i++) { scanf("%d %d", &S[i], &T[i]); --S[i], --T[i]; data[S[i]].insert(i); data[T[i]].insert(i); } bfs(); make_graph(); sort( edges.begin(), edges.end()); edges.erase(unique(edges.begin(), edges.end()), edges.end()); UnionFind uf(P); fill_n( answer, Q, -1); for(int i = 0; i < edges.size(); i++){ edge& e = edges[i]; if((e.u = uf.Find(e.u)) == (e.v = uf.Find(e.v))) continue; int unioned = uf.Union( e.u, e.v); int u = e.u, v = e.v; if(unioned == u) swap(u, v); for(set< int >::iterator it = data[u].begin(); it != data[u].end(); ++it){ set< int >::iterator cell = data[v].find(*it); if(cell != data[v].end()){ // あったらそのときのコストが解 answer[*it] = e.cost; } else { data[v].insert(*it); } } } for(int i = 0; i < Q; i++){ printf("%d\n", answer[i]); } }