AOJ2178 Futon

問題
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2534

N個の2*1マスの布団を広大なグリッド上に並べる。各々左上の座標と、縦と横のどちらの向きで配置するかの情報が与えられている。別の人の頭と足が隣り合わせになってしまう箇所が1つでも有れば、悪い配置となる。そうでなければ良い配置である。
良い配置かどうか判定せよ。
N <= 20000
座標は10^9以下


解説
布団の2マスと隣接する布団のマスとの間にグラフを張る。頭と足とで、同じものか違うものかの制限があるので、グラフの辺にその情報を加える。よって辺の情報は(隣接するマス, 同じか反対か)というようにすれば良い。
各連結成分ごとに、ある1つの頂点を頭か足か決め打ちしてDFSしたときに良い配置の方法で矛盾が生じないかを判定する。つまり、一度"頭"とメモしたところが、探索を進めて別の頂点から戻ってきたときに"足"とメモされる場合、矛盾となる。

別解として、Unionfindでグリッドグラフを連結してやる方法もあると思うが、自分は下手なやり方だったのでTLEしてしまった。再考察の必要がある。

int const OppoSide = 0;
int const SameSide = 1;
 
int N;
vector<pair<int, int>> G[44444];
map<pair<int, int>, int> MP;
map<int, pair<int, int>> CD;
int vis[44444];
 
bool dfs(int curr, int col) {
  for(auto& e: G[curr]) {
    int next, dir; tie(next, dir) = e;
    int nextCol = dir == OppoSide ? col ^ 1 : col;
    if(vis[next] + 1) {
      if(vis[next] != nextCol) return false;
    } else {
      vis[next] = nextCol;
      if(!dfs(next, nextCol)) return false;
    }
  }
  return true;
}
 
int main() {
 
  for(int M; cin >> M && M;) {
    vector<int> xs, ys;
    vector<int> X, Y; vector<char> D;
    rep(i, M) {
      int x, y; char d; cin >> x >> y >> d;
      X.push_back(x), Y.push_back(y), D.push_back(d);
 
      xs.push_back(x-1),  ys.push_back(y-1);
      xs.push_back(x),    ys.push_back(y);
      xs.push_back(x+1),  ys.push_back(y+1);
 
      if(d == 'x') {
        xs.push_back(x+2);
      } else {
        ys.push_back(y+2);
      }
 
    }
 
    sort(all(xs)); xs.erase(unique(all(xs)), xs.end());
    sort(all(ys)); ys.erase(unique(all(ys)), ys.end());
 
    vector<int> NX, NY;
    rep(i, X.size()) {
      NX.push_back(lower_bound(all(xs), X[i]) - X.begin());
    }
    rep(i, Y.size()) {
      NY.push_back(lower_bound(all(ys), Y[i]) - Y.begin());
    }
    X = NX, Y = NY;
 
    MP.clear(), CD.clear();
    rep(i, 44444) G[i].clear();
    N = 0;
 
    rep(i, X.size()) {
      MP[make_pair(Y[i], X[i])] = N;
      CD[N++] = make_pair(Y[i], X[i]);
      if(D[i] == 'x') {
        MP[make_pair(Y[i], X[i]+1)] = N;
        CD[N] = make_pair(Y[i], X[i]+1);
        G[N-1].emplace_back(N, OppoSide), G[N].emplace_back(N-1, OppoSide);
        N++;
      } else {
        MP[make_pair(Y[i]+1, X[i])] = N;
        CD[N] = make_pair(Y[i]+1, X[i]);
        G[N-1].emplace_back(N, OppoSide), G[N].emplace_back(N-1, OppoSide);
        N++;
      }
    }
 
    rep(i, N) {
      auto curr = CD[i];
      rep(k, 4) {
        auto next = make_pair(CD[i].first + dy[k], CD[i].second + dx[k]);
        if(MP.find(next) == MP.end()) continue;
        G[i].emplace_back(MP[next], SameSide);
      }
    }
 
    minus(vis);
 
    bool ok = 1;
 
    rep(i, N) {
      if(vis[i] < 0) {
        vis[i] = 0;
        if(!dfs(i, 0)) {
          ok = 0;
          break;
        }
      }
    }
 
    cout << (ok ? "Yes" : "No") << endl;
 
  }
  return 0;
}