Problem p01431 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <bits/stdc++.h>
using namespace std;

class UF {
public:
  int V;
  vector<int> par, rnk, sz;
  UF() {}
  UF(int V) : V(V), par(V), rnk(V, 0), sz(V, 1) {
    for (int i = 0; i < V; i++)
      par[i] = i;
  }

  int find(int x) {
    assert(x < V);
    if (par[x] == x)
      return x;
    return par[x] = find(par[x]);
  }

  void unite(int x, int y) {
    x = find(x), y = find(y);
    if (x == y)
      return;
    if (rnk[x] < rnk[y])
      par[x] = y, sz[y] += sz[x];
    else {
      par[y] = x;
      sz[x] += sz[y];
      if (rnk[x] == rnk[y])
        rnk[x]++;
    }
  }

  bool same(int x, int y) { return find(x) == find(y); }

  int size(int x) { return sz[find(x)]; }
};

int h, w;
string s[11];

unordered_set<int> can_go(int &A, int &pS, int &S,
                          UF &uf) { // SのAからiに行けるかどうか(ufでワープ可能)

  int C[2][11], used[2][11];

  for (int i = 0; i < 2; i++)
    for (int j = 0; j < w; j++)
      used[i][j] = 0;

  for (int i = 0; i < w; i++)
    C[0][i] = pS >> i & 1;
  for (int i = 0; i < w; i++)
    C[1][i] = S >> i & 1;

  typedef pair<int, int> P;

  queue<P> q;

  used[1][A] = 1;
  q.push(P(1, A));

  while (!q.empty()) {

    P tmp = q.front();
    q.pop();

    int idx = tmp.first;
    int node = tmp.second;

    int nidx = idx;
    int nnode = node + 1;

    if (nnode < w && C[nidx][nnode] && !used[nidx][nnode]) {
      used[nidx][nnode] = 1;
      q.push(P(nidx, nnode));
    }

    if (idx == 0) {

      nidx = idx + 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }

      for (int i = 0; i < w; i++) {

        if (uf.same(node, i) == 0)
          continue;

        nidx = 0;
        nnode = i;

        if (C[nidx][nnode] && !used[nidx][nnode]) {
          used[nidx][nnode] = 1;
          q.push(P(nidx, nnode));
        }
      }

    } else {

      nidx = idx - 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }
    }
  }

  unordered_set<int> res;

  for (int i = A + 1; i < w; i++)
    if (used[1][i])
      res.insert(i);

  return res;
}

bool roop(
    int &A, int &pS, int &S,
    UF &uf) { // Aの上のグループと同じところを繋いでしまうかどうか(ufでワープ可能)

  int C[2][11], used[2][11];

  for (int i = 0; i < 2; i++)
    for (int j = 0; j < w; j++)
      used[i][j] = 0;

  for (int i = 0; i < w; i++)
    C[0][i] = pS >> i & 1;
  for (int i = 0; i < w; i++)
    C[1][i] = S >> i & 1;

  typedef pair<int, int> P;

  queue<P> q;

  used[1][A] = 1;
  q.push(P(1, A));

  while (!q.empty()) {

    P tmp = q.front();
    q.pop();

    int idx = tmp.first;
    int node = tmp.second;

    if (idx == 0 && node == A)
      continue;

    if (node != A && idx == 1 && C[0][node] && uf.same(A, node) == 1)
      return true;

    int nidx = idx;
    int nnode = node + 1;

    if (nnode < w && C[nidx][nnode] && !used[nidx][nnode]) {
      used[nidx][nnode] = 1;
      q.push(P(nidx, nnode));
    }

    if (idx == 0) {

      nidx = idx + 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }

      for (int i = 0; i < w; i++) {

        if (uf.same(node, i) == 0)
          continue;

        nidx = 0;
        nnode = i;

        if (C[nidx][nnode] && !used[nidx][nnode]) {
          used[nidx][nnode] = 1;
          q.push(P(nidx, nnode));
        }
      }

    } else {

      nidx = idx - 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }
    }
  }

  return false;
}

bool AB(int &A, int &B, int &S) { // S上で、A と B が黒色で繋がっているかどうか

  for (int i = A; i <= B; i++)
    if ((S >> i & 1) == 0)
      return false;

  return true;
}

typedef unsigned long long ull;

ull UFtoULL(UF &uf) {

  ull res = 0, x = 1;

  for (int i = 0; i < w; i++) {

    for (int j = i + 1; j < w; j++) {

      if (uf.same(i, j))
        res += x;

      x *= 2;
    }
  }

  return res;
}

UF ULLtoUF(ull A) {

  UF res(w);
  ull cnt = 1;

  for (int i = 0; i < w; i++) {

    for (int j = i + 1; j < w; j++) {

      if (A >> cnt & 1)
        res.unite(i, j);

      cnt++;
    }
  }

  return res;
}

unordered_map<ull, int> memo[11][1 << 11];
char c[2] = {'.', '#'};

int dfs(int y, int pS, UF uf) {

  if (y == h) {

    for (int i = 0; i < w; i++) {

      if (!(pS >> i & 1))
        continue;

      for (int j = i + 1; j < w; j++) {

        if (!(pS >> j & 1))
          continue;

        if (uf.same(i, j) == 0)
          return -1e8;
      }
    }

    return 0;
  }

  if (memo[y][pS].count(UFtoULL(uf)))
    return memo[y][pS][UFtoULL(uf)];

  int res = -1e8;

  for (int S = 0; S < (1 << w); S++) {

    int flag = 1;

    for (int i = 0; i < w; i++) { // 矛盾がないか判定
      if (s[y][i] == '?')
        continue;
      if (c[S >> i & 1] != s[y][i])
        flag = 0;
    }

    if (flag == 0)
      continue;

    for (int i = 0; i < w; i++) { // 白色が隣合わないか判定
      if ((pS >> i & 1) == 0 && (S >> i & 1) == 0)
        flag = 0;
      if (i == w - 1)
        continue;
      if ((S >> i & 1) == 0 && (S >> (i + 1) & 1) == 0)
        flag = 0;
    }

    if (flag == 0)
      continue;

    if (y != 0) {

      for (int i = 0; i < w; i++) { // 分断されないか判定
        if ((pS >> i & 1) == 0)
          continue;
        if ((S >> i & 1) == 1)
          continue;
        int f = 0;
        for (int j = 0; j < w; j++) {
          if ((pS >> j & 1) == 0)
            continue;
          if ((S >> j & 1) == 0)
            continue;
          if (uf.same(i, j) == 1)
            f = 1;
        }
        if (f == 0)
          flag = 0;
      }

      if (flag == 0)
        continue;
    }

    if (y != 0) {

      for (int i = 0; i < w; i++) { // 閉路を作らないか判定

        if ((pS >> i & 1) == 0)
          continue;
        if ((S >> i & 1) == 0)
          continue;

        if (roop(i, pS, S, uf) == 1)
          flag = 0;
      }

      if (flag == 0)
        continue;
    }

    UF nuf(w);

    for (int i = 0; i < w; i++) { // どちらも存在する部分だけufからnufにコピー

      if ((pS >> i & 1) == 0)
        continue;
      if ((S >> i & 1) == 0)
        continue;

      for (int j = i + 1; j < w; j++) {

        if ((pS >> j & 1) == 0)
          continue;
        if ((S >> j & 1) == 0)
          continue;

        if (uf.same(i, j) == 1)
          nuf.unite(i, j);
      }
    }

    for (int i = 0; i < w; i++) { // 繋がる部分を連結にする

      if ((S >> i & 1) == 0)
        continue;

      unordered_set<int> node = can_go(i, pS, S, uf);

      for (int j = i + 1; j < w; j++) {

        if ((S >> j & 1) == 0)
          continue;

        if (y != 0 && node.count(j) == 1)
          nuf.unite(i, j);
        if (y == 0 && AB(i, j, S) == 1)
          nuf.unite(i, j);
      }
    }

    res = max(res, dfs(y + 1, S, nuf) + __builtin_popcount(S));
  }

  return memo[y][pS][UFtoULL(uf)] = res;
}

int main() {

  cin >> h >> w;

  for (int i = 0; i < h; i++)
    cin >> s[i];

  if (w == 1 && s[h - 1] == ".")
    h--;

  int ans = dfs(0, (1 << w) - 1, UF(w));

  if (ans < 0)
    cout << -1 << endl;
  else
    cout << ans << endl;

  return 0;
}


Error: TLE

Fixed submission:

#include <bits/stdc++.h>
using namespace std;

class UF {
public:
  int V;
  vector<int> par, rnk, sz;
  UF() {}
  UF(int V) : V(V), par(V), rnk(V, 0), sz(V, 1) {
    for (int i = 0; i < V; ++i)
      par[i] = i;
  }

  int find(int x) {
    assert(x < V);
    if (par[x] == x)
      return x;
    return par[x] = find(par[x]);
  }

  void unite(int x, int y) {
    x = find(x), y = find(y);
    if (x == y)
      return;
    if (rnk[x] < rnk[y])
      par[x] = y, sz[y] += sz[x];
    else {
      par[y] = x;
      sz[x] += sz[y];
      if (rnk[x] == rnk[y])
        rnk[x]++;
    }
  }

  bool same(int x, int y) { return find(x) == find(y); }

  int size(int x) { return sz[find(x)]; }
};

int h, w;
string s[11];

bool can_go(int &A, int &B, int &pS, int &S,
            UF &uf) { // SのAからBに行けるかどうか(ufでワープ可能)

  int C[2][11], used[2][11];

  for (int i = 0; i < 2; ++i)
    for (int j = 0; j < w; ++j)
      used[i][j] = 0;

  for (int i = 0; i < w; ++i)
    C[0][i] = pS >> i & 1;
  for (int i = 0; i < w; ++i)
    C[1][i] = S >> i & 1;

  typedef pair<int, int> P;

  queue<P> q;

  used[1][A] = 1;
  q.push(P(1, A));

  while (!q.empty()) {

    P tmp = q.front();
    q.pop();

    int idx = tmp.first;
    int node = tmp.second;

    int nidx = idx;
    int nnode = node + 1;

    if (idx == 1 && node == B)
      return true;

    if (nnode < w && C[nidx][nnode] && !used[nidx][nnode]) {
      used[nidx][nnode] = 1;
      q.push(P(nidx, nnode));
    }

    if (idx == 0) {

      nidx = idx + 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }

      for (int i = 0; i < w; ++i) {

        if (uf.same(node, i) == 0)
          continue;

        nidx = 0;
        nnode = i;

        if (C[nidx][nnode] && !used[nidx][nnode]) {
          used[nidx][nnode] = 1;
          q.push(P(nidx, nnode));
        }
      }

    } else {

      nidx = idx - 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }
    }
  }

  return used[1][B];
}

bool roop(
    int &A, int &pS, int &S,
    UF &uf) { // Aの上のグループと同じところを繋いでしまうかどうか(ufでワープ可能)

  int C[2][11], used[2][11];

  for (int i = 0; i < 2; ++i)
    for (int j = 0; j < w; ++j)
      used[i][j] = 0;

  for (int i = 0; i < w; ++i)
    C[0][i] = pS >> i & 1;
  for (int i = 0; i < w; ++i)
    C[1][i] = S >> i & 1;

  typedef pair<int, int> P;

  queue<P> q;

  used[1][A] = 1;
  q.push(P(1, A));

  while (!q.empty()) {

    P tmp = q.front();
    q.pop();

    int idx = tmp.first;
    int node = tmp.second;

    if (idx == 0 && node == A)
      continue;

    if (node != A && idx == 1 && C[0][node] && uf.same(A, node) == 1)
      return true;

    int nidx = idx;
    int nnode = node + 1;

    if (nnode < w && C[nidx][nnode] && !used[nidx][nnode]) {
      used[nidx][nnode] = 1;
      q.push(P(nidx, nnode));
    }

    if (idx == 0) {

      nidx = idx + 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }

      for (int i = 0; i < w; ++i) {

        if (uf.same(node, i) == 0)
          continue;

        nidx = 0;
        nnode = i;

        if (C[nidx][nnode] && !used[nidx][nnode]) {
          used[nidx][nnode] = 1;
          q.push(P(nidx, nnode));
        }
      }

    } else {

      nidx = idx - 1;
      nnode = node;

      if (C[nidx][nnode] && !used[nidx][nnode]) {
        used[nidx][nnode] = 1;
        q.push(P(nidx, nnode));
      }
    }
  }

  return false;
}

bool AB(int &A, int &B, int &S) { // S上で、A と B が黒色で繋がっているかどうか

  for (int i = A; i <= B; ++i)
    if ((S >> i & 1) == 0)
      return false;

  return true;
}

typedef unsigned long long ull;

ull UFtoULL(UF &uf) {

  ull res = 0, x = 1;

  for (int i = 0; i < w; ++i) {

    for (int j = i + 1; j < w; ++j) {

      if (uf.same(i, j))
        res += x;

      x *= 2;
    }
  }

  return res;
}

UF ULLtoUF(ull A) {

  UF res(w);
  ull cnt = 1;

  for (int i = 0; i < w; ++i) {

    for (int j = i + 1; j < w; ++j) {

      if (A >> cnt & 1)
        res.unite(i, j);

      cnt++;
    }
  }

  return res;
}

unordered_map<ull, int> memo[11][1 << 11];
char c[2] = {'.', '#'};

int dfs(int y, int pS, UF uf) {

  if (y == h) {

    for (int i = 0; i < w; ++i) {

      if (!(pS >> i & 1))
        continue;

      for (int j = i + 1; j < w; ++j) {

        if (!(pS >> j & 1))
          continue;

        if (uf.same(i, j) == 0)
          return -1e8;
      }
    }

    return 0;
  }

  if (memo[y][pS].count(UFtoULL(uf)))
    return memo[y][pS][UFtoULL(uf)];

  int res = -1e8;

  for (int S = 0; S < (1 << w); S++) {

    int flag = 1;

    for (int i = 0; i < w; ++i) { // 矛盾がないか判定
      if (s[y][i] == '?')
        continue;
      if (c[S >> i & 1] != s[y][i])
        flag = 0;
    }

    if (flag == 0)
      continue;

    for (int i = 0; i < w; ++i) { // 白色が隣合わないか判定
      if ((pS >> i & 1) == 0 && (S >> i & 1) == 0)
        flag = 0;
      if (i == w - 1)
        continue;
      if ((S >> i & 1) == 0 && (S >> (i + 1) & 1) == 0)
        flag = 0;
    }

    if (flag == 0)
      continue;

    if (y != 0) {

      for (int i = 0; i < w; ++i) { // 分断されないか判定
        if ((pS >> i & 1) == 0)
          continue;
        if ((S >> i & 1) == 1)
          continue;
        int f = 0;
        for (int j = 0; j < w; ++j) {
          if ((pS >> j & 1) == 0)
            continue;
          if ((S >> j & 1) == 0)
            continue;
          if (uf.same(i, j) == 1)
            f = 1;
        }
        if (f == 0)
          flag = 0;
      }

      if (flag == 0)
        continue;
    }

    if (y != 0) {

      for (int i = 0; i < w; ++i) { // 閉路を作らないか判定

        if ((pS >> i & 1) == 0)
          continue;
        if ((S >> i & 1) == 0)
          continue;

        if (roop(i, pS, S, uf) == 1)
          flag = 0;
      }

      if (flag == 0)
        continue;
    }

    UF nuf(w);

    for (int i = 0; i < w; ++i) { // どちらも存在する部分だけufからnufにコピー

      if ((pS >> i & 1) == 0)
        continue;
      if ((S >> i & 1) == 0)
        continue;

      for (int j = i + 1; j < w; ++j) {

        if ((pS >> j & 1) == 0)
          continue;
        if ((S >> j & 1) == 0)
          continue;

        if (uf.same(i, j) == 1)
          nuf.unite(i, j);
      }
    }

    for (int i = 0; i < w; ++i) { // 繋がる部分を連結にする

      if ((S >> i & 1) == 0)
        continue;

      for (int j = i + 1; j < w; ++j) {

        if ((S >> j & 1) == 0)
          continue;

        if (y != 0 && can_go(i, j, pS, S, uf) == 1)
          nuf.unite(i, j);
        if (y == 0 && AB(i, j, S) == 1)
          nuf.unite(i, j);
      }
    }

    res = max(res, dfs(y + 1, S, nuf) + __builtin_popcount(S));
  }

  return memo[y][pS][UFtoULL(uf)] = res;
}

int main() {

  cin >> h >> w;

  for (int i = 0; i < h; ++i)
    cin >> s[i];

  if (w == 1 && s[h - 1] == ".")
    h--;

  int ans = dfs(0, (1 << w) - 1, UF(w));

  if (ans < 0)
    cout << -1 << endl;
  else
    cout << ans << endl;

  return 0;
}