2015年2月27日金曜日

JOI春合宿2014 Day2-1 Water Bottle

解法

隣り合っている建物について辺を張って,最小全域木作って,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]);
  }
}

0 件のコメント:

コメントを投稿