mayoko’s diary

プロコンとかいろいろ。

TCO15 Round 2B med:Balance

本番は問題文が読めなくて非常にイライラした問題(多分解けなかったけど)。こんな複雑な処理みんなよく短時間で書けるなぁ。

解法

kmjpさんの解説を参考にしました。kmjp.hatenablog.jp

hashを適当に作ったからか,一つのハッシュだけで勝負すると後半の方のテストケースでWAが出ました。

const int dx[] = {1, 0, -1, 0};
const int dy[] = {0, 1, 0, -1};

bool used[55*55];

struct UnionFind {
    vector<int> par;
    int n, cnt;
    UnionFind(const int& x = 0) {init(x);}
    void init(const int& x) {par.assign(cnt=n=x, -1);}
    inline int find(const int& x) {return par[x] < 0 ? x : par[x] = find(par[x]);}
    inline bool same(const int& x, const int& y) {return find(x) == find(y);}
    inline bool unite(int x, int y) {
        if ((x = find(x)) == (y = find(y))) return false;
        --cnt;
        if (par[x] > par[y]) swap(x, y);
        par[x] += par[y];
        par[y] = x;
        return true;
    }
    inline int count() const {return cnt;}
    inline int count(int x) {return -par[find(x)];}
};

vector<string> changeBoard(const vector<string>& board) {
    int n = board.size();
    string s;
    for (int i = 0; i < n+2; i++) s += "#";
    vector<string> ret(n+2);
    ret[0] = s;
    for (int i = 0; i < n; i++) ret[i+1] = "#" + board[i] + "#";
    ret[n+1] = s;
    return ret;
}

UnionFind uf;
map<int, int> M;

void dfs(int y, int x, const vector<string>& board, vector<vector<int> >& ret) {
    int n = board.size();
    int group = M[uf.find(y*n+x)];
    used[group] = true;
    for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) {
        if (uf.same(y*n+x, i*n+j)) {
            for (int k = 0; k < 4; k++) {
                int ny = i+dy[k];
                int nx = j+dx[k];
                if (ny < 0 || ny >= n || nx < 0 || nx >= n) continue;
                int tmp = M[uf.find(ny*n+nx)];
                if (!used[tmp]) {
                    ret[group].push_back(tmp);
                    dfs(ny, nx, board, ret);
                }
            }
        }
    }
}

vector<vector<int> > createTree(const vector<string> board) {
    int n = board.size();
    uf.init(n*n);
    // unionfind木を構成する
    for (int y = 0; y < n; y++) {
        for (int x = 0; x < n; x++) {
            for (int k = 0; k < 4; k++) {
                int ny = y+dy[k];
                int nx = x+dx[k];
                if (ny < 0 || ny >= n || nx < 0 || nx >= n) continue;
                if (board[y][x] == board[ny][nx]) {
                    uf.unite(y*n+x, ny*n+nx);
                }
            }
        }
    }
    // unionfindのサイズ
    int k = 0;
    M.clear();
    for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) {
        int tmp = uf.find(i*n+j);
        if (M.find(tmp) == M.end()) {
            M.insert(make_pair(tmp, k++));
        }
    }
    vector<vector<int> > ret(k);
    // dfsして内包関係を木グラフに落としこむ
    memset(used, false, sizeof(used));
    dfs(0, 0, board, ret);
    return ret;
}

pair<ll, ll> getHash(int cur, const vector<vector<int> > tree) {
    ll mod[2] = {1000000007, 1000000009};
    pair<ll, ll> ret = make_pair(1145141919, 5963);
    vector<pair<ll, ll> > hh;
    for (int i = 0; i < (int)tree[cur].size(); i++) {
        hh.push_back(getHash(tree[cur][i], tree));
    }
    sort(hh.begin(), hh.end());
    for (auto el : hh) {
        ret.first = ret.first*65535 + el.first;
        ret.first *= 373;
        ret.first %= mod[0];
        ret.second = ret.second*65535 + el.second;
        ret.second *= 373;
        ret.second %= mod[1];
    }
    return ret;
}

class Balance {
public:
    string canTransform(vector <string> initial, vector <string> target) {
        cout << "TEST" << endl;
        initial = changeBoard(initial);
        target = changeBoard(target);
        auto treei = createTree(initial);
        auto treet = createTree(target);
        // 木の同型性を判定する
        if (getHash(0, treei) == getHash(0, treet)) return "Possible";
        else return "Impossible";
    }
};