【ATcode】 AtCoDeerくんと立方体づくり / Building Cubes with AtCoDeer(模拟)

题目链接                             有翻译的

有N块瓷砖,编号从1到N,并且将这个编号写在瓷砖的正中央;瓷砖的四个角上分别有四种颜色(可能相等可能不想等),并且用C_{i,0},C_{i,1},C_{i,2},C_{i,3}Ci,0​,Ci,1​,Ci,2​,Ci,3​分别表示左上、右上、右下、左下的颜色。颜色有1000种,编号从0到999。现在想知道,从这N块瓷砖中选出不同的6块,能围成多少本质不同的合法的立方体。 一个立方体被成为合法的,当且仅当瓷砖有编号的一侧在外面,并且立方体的每个顶点处的三个颜色相同。 注意,由于瓷砖的中间是写着编号的,因此将一个瓷砖旋转90度之后,这个瓷砖会发生变化。 也就是说一块瓷砖可以被用作四个方向(哪怕旋转后四个角的颜色对应相等)。 两个立方体被称作是本质相同的,当且仅当存在在空间中旋转地一个立方体的方式,使得其和第二个立方体一模一样(包括每面瓷砖上编号的方向)。

思路:一开始没什么思路,觉得是个组合数来着,后来发现需要核对的点是很多的,情况也是很多,

后来看了网上大神的题解后,明白了这个题目。

这个题目的话,n<=400,放心大胆的去暴力模拟,

是这样的,正方体,6个面组成,我们可以调出来一个相对的面,比如说是下面的2和4,1和5,3和6这样子的

然后我们就可以利用这两个面来对其他的后来拼上去的面做一个判断,因为一个面有4种情况的。有的情况是不行的,可能组不成一个正方体,那么我们就利用以及拼好的面来判断后来拼上去的面,

还有就是最终的结果,是除以3的,可以想一下,我们从6个中取出两个来,比如拿出的是a1,a2两个面,a3,a4做我们这次的拼接上去的面

那么是不是如果我们下一次拿出来的a3,a4,a1a2也会去做一次拼接上去的面呢,类推

这题反正弄了我很久吧,一个模拟,

代码:

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<map>
#define ll long long
using namespace std;
int n;
ll ans;
struct Node
{
    int a[4];
    Node(){}
    Node(int x,int y,int z,int w)
    {
        a[0]=x,a[1]=y,a[2]=z,a[3]=w;
    }
    bool operator < (const Node &ags) const
    {
        if(a[0]!=ags.a[0])
            return a[0]<ags.a[0];
        if(a[1]!=ags.a[1])
            return a[1]<ags.a[1];
        if(a[2]!=ags.a[2])
            return a[2]<ags.a[2];
        return a[3]<ags.a[3];
    }
    bool operator == (const Node &ags) const
    {
        for(int i=0;i<4;i++)
            if(a[i]!=ags.a[i])
                return false;
        return true;
    }
}t[100010];
map<Node,ll >mp;
void AddNode(int s1,int s2,int s3,int s4)
{
    mp[Node(s1,s2,s3,s4)]++;
}
ll judge(Node x,Node y)
{
    ll res=0;
    if(x==y) //1
        res++;

    int temp=y.a[0];
    for(int i=0;i<3;i++)
        y.a[i]=y.a[i+1];
    y.a[3]=temp;
    if(x==y) //2
        res++;

    temp=y.a[0];
    for(int i=0;i<3;i++)
        y.a[i]=y.a[i+1];
    y.a[3]=temp;
    if(x==y) //3
        res++;

    temp=y.a[0];
    for(int i=0;i<3;i++)
        y.a[i]=y.a[i+1];
    y.a[3]=temp;
    if(x==y) //4
        res++;
    return res;
}
void jisuan(int b1,int b2,int b3,int b4,int b5,int b6,int b7,int b8)
{
    Node x1=Node(b1,b2,b6,b5);
    Node x2=Node(b2,b3,b7,b6);
    Node x3=Node(b3,b4,b8,b7);
    Node x4=Node(b4,b1,b5,b8);
    Node sp1=Node(b4,b3,b2,b1);
    Node sp2=Node(b5,b6,b7,b8);
    ll res1=mp[x1];
    ll res2=mp[x2];
    ll res3=mp[x3];
    ll res4=mp[x4];

    res1=res1-judge(sp1,x1)-judge(sp2,x1);
    res2=res2-judge(sp1,x2)-judge(sp2,x2)-judge(x1,x2);
    res3=res3-judge(sp1,x3)-judge(sp2,x3)-judge(x1,x3)-judge(x2,x3);
    res4=res4-judge(sp1,x4)-judge(sp2,x4)-judge(x1,x4)-judge(x2,x4)-judge(x3,x4);

    ans+=res1*res2*res3*res4;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<4;j++)
            scanf("%d",&t[i].a[j]);

        AddNode(t[i].a[3],t[i].a[2],t[i].a[1],t[i].a[0]);
        AddNode(t[i].a[2],t[i].a[1],t[i].a[0],t[i].a[3]);
        AddNode(t[i].a[1],t[i].a[0],t[i].a[3],t[i].a[2]);
        AddNode(t[i].a[0],t[i].a[3],t[i].a[2],t[i].a[1]);
    }
    ans=0;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            jisuan(t[i].a[0],t[i].a[1],t[i].a[2],t[i].a[3], t[j].a[3],t[j].a[2],t[j].a[1],t[j].a[0]);
            jisuan(t[i].a[0],t[i].a[1],t[i].a[2],t[i].a[3], t[j].a[2],t[j].a[1],t[j].a[0],t[j].a[3]);
            jisuan(t[i].a[0],t[i].a[1],t[i].a[2],t[i].a[3], t[j].a[1],t[j].a[0],t[j].a[3],t[j].a[2]);
            jisuan(t[i].a[0],t[i].a[1],t[i].a[2],t[i].a[3], t[j].a[0],t[j].a[3],t[j].a[2],t[j].a[1]);
        }
    printf("%lld",ans/3ll);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值