CodeForces - 1008D - Pave the Parallelepiped (容斥原理+重复组合公式+状态压缩+思维)

题目链接:

http://codeforces.com/problemset/problem/1008/D

前置知识:

1.容斥原理:(见百度百科)

https://baike.baidu.com/item/%E5%AE%B9%E6%96%A5%E5%8E%9F%E7%90%86/10146840?fr=aladdin

2.重复组合公式(刘汝佳—算法竞赛入门经典)

有n个不同元素,每个元素可以选多次,一共选k个元素,有多少种方法?例如,n=3,k=2时有6种:(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)。

分析:

设第 i 个元素选 xi 个,问题转化为求方程x1+x2+x3+...+xn=k的非负整数解的个数。令yi=xi+1,则答案为y1+y2+y3+...+yn=k+n的正整数解的个数。想象有k+n个数字“1”排成一排,则问题等价于:把这些“1”分成n个部分,有多少种方法?这相当于在k+n-1个“候选分隔线”中选n-1个,即C(k+n-1,n-1)=C(n+k-1,k)

3.几个数的公约数的个数,也是他们最大公约数的因数个数。

题目

You are given a rectangular parallelepiped with sides of positive integer lengths A, B and C.

Find the number of different groups of three integers (a, b, c) such that 1≤a≤b≤c and parallelepiped A×B×C can be paved with parallelepipeds a×b×c. Note, that all small parallelepipeds have to be rotated in the same direction.

For example, parallelepiped 1×5×6 can be divided into parallelepipeds 1×3×5, but can not be divided into parallelepipeds 1×2×3.

Input

The first line contains a single integer tt (1≤t≤105) — the number of test cases.

Each of the next tt lines contains three integers A, B and C (1≤A,B,C≤105) — the sizes of the parallelepiped.

Output

For each test case, print the number of different groups of three points that satisfy all given conditions.

Example

Input

4
1 1 1
1 6 1
2 2 2
100 100 100

Output

1
4
4
165

Note

In the first test case, rectangular parallelepiped (1,1,1) can be only divided into rectangular parallelepiped with sizes (1,1,1).

In the second test case, rectangular parallelepiped (1,6,1) can be divided into rectangular parallelepipeds with sizes (1,1,1), (1,1,2), (1,1,3) and (1,1,6).

In the third test case, rectangular parallelepiped (2,2,2) can be divided into rectangular parallelepipeds with sizes (1,1,1), (1,1,2), (1,2,2) and (2,2,2).

题目大意:

T次询问,每次询问给出一个A,B,C,表示一个A×B×C大小的长方体,如果有一种小长方体(表示为a×b×c)(1≤a≤b≤c)能够铺满整个大长方体,这种小长方体就符合条件(要求小的长方体必须以同样的方向来铺),问你有多少种符合条件的小长方体。

题目分析:

将题意转化一下就是,给你三个数A,B,C,问你从这三个数的因子中能挑选出多少种不同的组合

看到这个问题,我们首先想到的是直接把A,B,C,的因子数乘起来然后去重,可是这样的话,对于我这样的蒟蒻,无疑是非常困难的。那么我们应该怎么做呢?我们可以先把因子进行预处理,分好类,再进行计算。我们可以用3个二进制位表示A,B,C因子的状态,比如,我们让第一位二进制位表示是否为A的因子(1代表是,0代表不是,下同),第二位二进制位表示是否为B的因子,第三位二进制位表示是否为C的因子,这样我们就用7个数表示了因子的所有状态。如下:

001代表只是A的因子的数量

010代表只是B的因子的数量

011代表只是A的因子和B的因子的数量(不是C的因子)

100代表只是C的因子的数量

101代表只是A的因子和C的因子的数量(不是B的因子)

110代表只是A的因子和B的因子的数量(不是C的因子)

111代表同时是A,B,C的因子的数量

注意:上面这些都强调只是

那么这些类别的数量我们应该怎么计算呢,当然,在开始之前我们需要把 n 之前的所有因子的数量打表统计出来。

然后如下图:

 

又因为:几个数的公约数的个数,也是他们最大公约数的因数个数,所以我们通过简单的加减运算可以得到图中每一部分的大小。(自己体会)

//ft代表分好的类 fac中存的是元素因子的个数
ft[1]=fac[a]-fac[gcdab]-fac[gcdac]+fac[gcdabc];
ft[2]=fac[b]-fac[gcdab]-fac[gcdbc]+fac[gcdabc];
ft[4]=fac[c]-fac[gcdac]-fac[gcdbc]+fac[gcdabc];
ft[3]=fac[gcdab]-fac[gcdabc];
ft[5]=fac[gcdac]-fac[gcdabc];
ft[6]=fac[gcdbc]-fac[gcdabc];
ft[7]=fac[gcdabc];

到这里,我们已经吧分类的工作处理好了,接下来就是计算了。因为这7类里面的元素是互不相等的,所以我们可以枚举这7类,只要是选出这些元素的三类,可以使三类分别对应A,B,C的因子,即可。如果从两个或者三个的都来自相同的类别,我们就要用重复组合公式(处理方式是在最内层的循环里提前记录一下每个元素来自的类别及出现次数)。

bool check(int a,int b,int c)
{
    if((a&1)&&((b>>1)&1)&&((c>>2)&1))
        return true;
    if((a&1)&&((c>>1)&1)&&((b>>2)&1))
        return true;
    if((b&1)&&((a>>1)&1)&&((c>>2)&1))
        return true;
    if((b&1)&&((c>>1)&1)&&((a>>2)&1))
        return true;
    if((c&1)&&((a>>1)&1)&&((b>>2)&1))
        return true;
    if((c&1)&&((b>>1)&1)&&((a>>2)&1))
        return true;
    return false;
}
    for(int i=1;i<8;i++)
            for(int j=i;j<8;j++)
                for(int k=j;k<8;k++)
                {
                    if(check(i,j,k))
                    {
                        memset(num,0,sizeof(num));
                        num[i]++;num[j]++;num[k]++;
                        long long temp=1;
                        bool flag=false;
                        for(int l=1;l<8;l++)
                        {
                            if(num[l])
                            {
                                if(ft[l]+num[l]-1>0)
                                    temp*=C[ft[l]+num[l]-1][num[l]];
                                else
                                    flag=true;
                            }
                        }
                        if(!flag)//不要忘记处理
                            ans+=temp;
                    }
                }

完整代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>

using namespace std;
const int MAXN=1e5+10;
long long fac[MAXN],ft[8];
long long C[1000][1000];
int num[8];

void Init()
{
    for(int i=1;i<MAXN;i++)
        for(int j=i;j<MAXN;j+=i)
            fac[j]++;

    memset(C,0,sizeof(C));
    for(int i=0;i<1000;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];
    }
}

bool check(int a,int b,int c)
{
    if((a&1)&&((b>>1)&1)&&((c>>2)&1))
        return true;
    if((a&1)&&((c>>1)&1)&&((b>>2)&1))
        return true;
    if((b&1)&&((a>>1)&1)&&((c>>2)&1))
        return true;
    if((b&1)&&((c>>1)&1)&&((a>>2)&1))
        return true;
    if((c&1)&&((a>>1)&1)&&((b>>2)&1))
        return true;
    if((c&1)&&((b>>1)&1)&&((a>>2)&1))
        return true;
    return false;
}

int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}

int main()
{
    Init();
    int T;
    scanf("%d",&T);
    int a,b,c,gcdab,gcdac,gcdbc,gcdabc;
    while(T--)
    {
        scanf("%d%d%d",&a,&b,&c);
        gcdab=gcd(a,b);
        gcdac=gcd(a,c);
        gcdbc=gcd(b,c);
        gcdabc=gcd(gcdab,c);
        ft[1]=fac[a]-fac[gcdab]-fac[gcdac]+fac[gcdabc];
        ft[2]=fac[b]-fac[gcdab]-fac[gcdbc]+fac[gcdabc];
        ft[4]=fac[c]-fac[gcdac]-fac[gcdbc]+fac[gcdabc];
        ft[3]=fac[gcdab]-fac[gcdabc];
        ft[5]=fac[gcdac]-fac[gcdabc];
        ft[6]=fac[gcdbc]-fac[gcdabc];
        ft[7]=fac[gcdabc];

        long long ans=0;
        for(int i=1;i<8;i++)
            for(int j=i;j<8;j++)
                for(int k=j;k<8;k++)
                {
                    if(check(i,j,k))
                    {
                        memset(num,0,sizeof(num));
                        num[i]++;num[j]++;num[k]++;
                        long long temp=1;
                        bool flag=false;
                        for(int l=1;l<8;l++)
                        {
                            if(num[l])
                            {
                                if(ft[l]+num[l]-1>0)
                                    temp*=C[ft[l]+num[l]-1][num[l]];
                                else
                                    flag=true;
                            }
                        }
                        if(!flag)//不要忘记处理
                            ans+=temp;
                    }
                }
        printf("%lld\n",ans);
    }
    return 0;
}

END

不得不说,刚开始智障,没有想到处理方法,就手写枚举了64种情况,233。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值