AT2142 [ARC062E] Building Cubes with AtCoDeer

发布于 2020-11-26  99 次阅读


Link

Sol

事实上确定一个立方体只需要确定上下两个面就可以了(这样子每个角的颜色就唯一确定了)

讨论一下不同情况的瓷砖

一块瓷砖有可能四个角颜色都相同,那么它随意旋转都可以对答案贡献1, 所以这种瓷砖的贡献是4

一块形似aabb的瓷砖,也就是每次旋转180\circ 颜色相同, 那么它对答案的贡献是 2

如果都不相同对答案的贡献就是 1

考虑统计答案的方式,我们每次枚举上下两个面,那么随之其他面也被确定

可以用一个哈希表,把一种瓷砖本身\旋转和全部倒过来的都存进哈希表,每次取出两个面的时候乘法原理算一下答案即可

Code

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

#define int long long

const int SIZE = 5e2 + 5;
const int key = 2002;
const int mod = 998244853;

int n, ans;
int hsh[SIZE], p[5], q[5];

std::unordered_map < int, pair < int, int > > s;

namespace ae86 {
    const int bufl = 1 << 15;
    char buf[bufl], *s = buf, *t = buf;
    inline int fetch() {
        if (s == t) { t = (s = buf) + fread(buf, 1, bufl, stdin); if (s == t) return EOF; }
        return *s++;
    }
    inline int read() {
        int a = 0, b = 1, c = fetch();
        while (!isdigit(c))b ^= c == '-', c = fetch();
        while (isdigit(c)) a = a * 10 + c - 48, c = fetch();
        return b ? a : -a;
    }
}
using ae86::read;

inline int getHash(int a, int b, int c, int d) {
    return a * key * key * key + b * key * key + c * key + d;
}

namespace tony102 {
    inline int swap(int x) { 
        return x % (key * key) * key * key + x / key / key;
    }

    inline int reverse(int x) {
        return x % key * key * key * key + x / key;
    }

    inline int min(int x) {
        int t = swap(x);
        return std::min(std::min(t, x), std::min(reverse(t), reverse(x)));
    }
}

inline int getVal(int x, int opt) {
    return x / q[opt] % key;
}

inline void solve(int x, int y, int tim) {
    p[1] = tony102::min(getHash(getVal(x, 1), getVal(y, 2), getVal(y, 1), getVal(x, 2)));
    if (!s[p[1]].second) return;
    p[2] = tony102::min(getHash(getVal(x, 2), getVal(y, 1), getVal(y, 4), getVal(x, 3)));
    if (!s[p[2]].second) return;
    p[3] = tony102::min(getHash(getVal(x, 3), getVal(y, 4), getVal(y, 3), getVal(x, 4)));
    if (!s[p[3]].second) return;
    p[4] = tony102::min(getHash(getVal(x, 4), getVal(y, 3), getVal(y, 2), getVal(x, 1)));
    if (!s[p[4]].second) return;
    int cnt = 1;
    for (int i = 1; i <= 4; ++ i) {
        cnt = (s[p[i]].first * s[p[i]].second) * cnt;
        -- s[p[i]].second;
    }
    for (int i = 1; i <= 4; ++ i) ++ s[p[i]].second;
    cnt = cnt * tim;
    ans = (ans + cnt);
}

signed main() {
    // freopen("intermezzo.in", "r", stdin);
    // freopen("intermezzo.out", "w", stdout);
    n = read(), q[4] = 1;
    for (int i = 3; i; -- i) q[i] = q[i + 1] * key;
    for (int i = 1, c0, c1, c2, c3; i <= n; ++ i) {
        c0 = read(), c1 = read(), c2 = read(), c3 = read();
        int h = getHash(c0, c1, c2, c3);
        h = tony102::min(h);
        if (h == tony102::reverse(h)) s[h].first = 4;
        else {
            if (h == tony102::swap(h)) s[h].first = 2;
            else s[h].first = 1;
        }
        ++ s[h].second, hsh[i] = h;
    }
    for (int i = 1; i < n - 2; ++ i) {
        -- s[hsh[i]].second;
        for (int j = i + 1; j <= n; ++ j) {
            -- s[hsh[j]].second;
            if (s[hsh[i]].first == 1) {
                solve(hsh[i], hsh[j], 1);
                solve(hsh[i], tony102::reverse(hsh[j]), 1);
                solve(hsh[i], tony102::swap(hsh[j]), 1);
                solve(hsh[i], tony102::reverse(tony102::swap(hsh[j])), 1);
            }
            else if (s[hsh[i]].first == 2) {
                solve(hsh[i], hsh[j], 2);
                solve(hsh[i], tony102::reverse(hsh[j]), 2);
            }
            else if (s[hsh[i]].first == 4) solve(hsh[i], hsh[j], 4);
            ++ s[hsh[j]].second;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

学会生存 学会关心