【AtCoder Regular Contest 062 E】Building Cubes with AtCoDeer

枚举。

题目大意:

给定 $n$ 个面,每个面有数字,四个角上有颜色。一个面可以旋转,所以有 $4$ 种本质不同的方案。

现在问可以构成多少个本质不同的立方体。如果两个立方体能通过旋转同构则是相同的。

构成立方体要六个面,且每个角上的颜色要相等。

题解:

unsigned long long 可以把颜色的状态压缩起来。用 map 来计数。

我们可以枚举前、后两个面,这样所有角的颜色就确定了,每个面的状态就确定了。

然后直接乘法原理合并答案即可。

一个立方体在三对面中会被计算,每对被计算 $4$ 次,所以最后要除以 $12$。

注意如果出现四个角相等或对角相等的面,则计数时需要额外乘上这个面旋转后角相等的方案数。

时间复杂度 $O(n^2\log n)$。

Code:

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef unsigned long long LL;
map<LL,int>c;
const int N=404;
struct zk{
    int a[4];
    inline zk(int A,int B,int C,int D){a[0]=A,a[1]=B,a[2]=C,a[3]=D;}
    inline zk(){}
    inline LL Hash(){
        LL r=0x3fffffffffffffff;
        for(int i=0;i<4;++i)
        r=min(r,((LL)a[i]<<48)|((LL)a[(i+1)&3]<<32)|((LL)a[(i+2)&3]<<16)|(a[(i+3)&3]));
        return r;
    }
    inline int check(){
        if(a[0]==a[1]&&a[1]==a[2]&&a[2]==a[3])return 4;
        if(a[0]==a[2]&&a[1]==a[3])return 2;
        return 1;
    }
}A[N];
inline istream&operator>>(istream&in,zk&a){in>>a.a[0]>>a.a[1]>>a.a[2]>>a.a[3];return in;}
int n;LL ans;
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)cin>>A[i],++c[A[i].Hash()];
    for(int X=1;X<=n;++X)
    for(int X_=0;X_<4;++X_)
    for(int Y=X+1;Y<=n;++Y)
    for(int Y_=0;Y_<4;++Y_){
        zk H=zk(A[X].a[X_],A[X].a[(X_+1)&3],A[X].a[(X_+2)&3],A[X].a[(X_+3)&3]);
        zk T=zk(A[Y].a[Y_],A[Y].a[(Y_+1)&3],A[Y].a[(Y_+2)&3],A[Y].a[(Y_+3)&3]);
        --c[H.Hash()],--c[T.Hash()];
        zk L=zk(T.a[1],H.a[0],H.a[3],T.a[2]),R=zk(H.a[1],T.a[0],T.a[3],H.a[2]);
        zk U=zk(T.a[1],T.a[0],H.a[1],H.a[0]),D=zk(H.a[3],H.a[2],T.a[3],T.a[2]);
        LL r=1;
        r*=c[L.Hash()]--;
        r*=c[R.Hash()]--;
        r*=c[U.Hash()]--;
        r*=c[D.Hash()]--;
        ++c[L.Hash()],++c[R.Hash()],++c[U.Hash()],++c[D.Hash()];
        ++c[H.Hash()],++c[T.Hash()];
        r*=L.check()*R.check()*U.check()*D.check();
        ans+=r;
    }
    cout<<ans/12<<'\n';
    return 0;
}