【arc062e】Building Cubes with AtCoDeer
Description
STL有n块瓷砖,编号从1到n,并且将这个编号写在瓷砖的正中央;
瓷砖的四个角上分别有四种颜色(可能相等可能不相等),并且用Ci,0,Ci,1,Ci,2,Ci,3分别表示左上、右上、右下、左下的颜色。颜色有1000种,编号从0到999。
现在STL想知道,从这n块瓷砖中选出不同的6块,能围成多少本质不同的合法的立方体。
一个立方体被称为合法的,当且仅当瓷砖有编号的一侧在外面,并且立方体的每个顶点处的三个颜色相同。
注意,由于瓷砖的中间是写着编号的,因此将一个瓷砖旋转90度之后,这个瓷砖会发生变化,也就是说一块瓷砖可以被用作四个方向(哪怕旋转后四个角的颜色对应相等)。
两个立方体被称作是本质相同的,当且仅当存在在空间中旋转一个立方体的方式,使其和第二个立方体一模一样(包括每面瓷砖上编号的方向)。
Solution
n最大为400,显然可以暴力解决。
我们可以发现,当一个立方体中,只要确定了任意两个相对的面,就可唯一确定整个立方体。
因此我们枚举相对两面编号及编号方向,用map储存瓷砖,计算方案即可。
ps:由于正方形可旋转,因此存入map时,要将旋转4次的结果都存入map中。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<map> 5 using namespace std; 6 #define hash func 7 typedef long long ll; 8 int n,c[410][4]; 9 ll ans=0,h[410]; 10 map<ll,int>mp; 11 ll hash(ll a,ll b,ll c,ll d){ 12 return a<<30|b<<20|c<<10|d; 13 } 14 void add(ll x,int k){ 15 for(int i=4;i;x=x>>10|(x&1023)<<30,i--) 16 mp[x]+=k; 17 return; 18 } 19 int main(){ 20 mp.clear(); 21 scanf("%d",&n); 22 for(int i=1;i<=n;i++){ 23 scanf("%d%d%d%d",c[i]+0,c[i]+1,c[i]+2,c[i]+3); 24 h[i]=hash(c[i][0],c[i][1],c[i][2],c[i][3]); 25 add(h[i],1); 26 } 27 for(int i=1;i<n-4;i++){ 28 add(h[i],-1); 29 for(int j=i+1;j<=n;j++){ 30 add(h[j],-1); 31 for(int k=0;k<4;k++){ 32 ll a[4]; 33 a[0]=hash(c[i][3],c[i][2],c[j][(k+1)%4],c[j][k]); 34 a[1]=hash(c[i][2],c[i][1],c[j][(k+2)%4],c[j][(k+1)%4]); 35 a[2]=hash(c[i][1],c[i][0],c[j][(k+3)%4],c[j][(k+2)%4]); 36 a[3]=hash(c[i][0],c[i][3],c[j][k],c[j][(k+3)%4]); 37 if(mp[a[0]]==0||mp[a[1]]==0||mp[a[2]]==0||mp[a[3]]==0) 38 continue; 39 ll res=1; 40 for(int l=0;l<4;l++){ 41 res*=mp[a[l]]; 42 add(a[l],-1); 43 } 44 ans+=res; 45 for(int l=0;l<4;l++) 46 add(a[l],1); 47 } 48 add(h[j],1); 49 } 50 } 51 printf("%lld\n",ans); 52 return 0; 53 }