Pave the Parallelepiped(有重复组合公式+状态压缩)

Pave the Parallelepiped

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 t

(1≤t≤105

) — the number of test cases.

Each of the next t
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).

题意:

有一个 A × B × C 的长方体,将其分成 a × b × c 的小正方体,其中 a b c 问这样的abc小正方体能有多少种?

分析:

首先我们能够想到的就是abc至少一个是A的因子,一个是B的因子,一个是C的因子。

如果直接用组合数学进行推公式的话,需要考虑的情况太多,细节太多,一般人绝对考虑不全。

我们整体考虑对于a,b,c每个数无非有7中情况,所以我们考虑用三位二进制来表示状态

001 是A的因数
010 是B的因数
011 是A的因数也是B的因数即是gcd(A,B)的因数
100 是C的因数
101 是A的因数也是C的因数即是gcd(A,C)的因数
110 是B的因数也是C的因数即是gcd(B,C)的因数
111 是A的因数也是B的因数也是C的因数即是gcd(A,B,C)的因数

根据题意我们完全可以预处理处最大范围内每个数的因子个数,然后每个样例对于所给的A,B,C
求出gcd(A,B),gcd(A,C),gcd(B,C);
我们便可以利用容斥定理求出7种状态的因子个数cnt[i](i = 1,2,…7)

这样对于a,b,c我们只需要枚举每个数的状态(三重循环),并判断保证三个数至少有一个是A的因子B的因子C的因子,这样我们就得到了满足条件的a,b,c的个数。

1.如果说a,b,c都是不同的状态,那么我们就得到三种数量,从每种数量中选择1个,利用组合数很好求

2.但是如果有两个数或者三个数相同的状态,这就要求我们从一定数量中选择有重复个元素举个例子
如果ab状态相同,都是cnt[s],那么我们应该从cnt[s]中选两个,并且可以重复即a选了k,b也选了k
这就需用到有重复的组合公式:
如果从n个元素中选择r个有重复元素,其公式为:

C n + r 1 r

此链接为证明过程

这样对于枚举的一种abc状态,各个数的种类数相乘即可,最后每种abc状态个数相加即为最终答案

是个组合数学好题

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
ll C(int n,int m){//求组合数
    ll ans = 1;
    for(int i = 1; i <= m; i++){
        ans = ans * (n-i+1) / i;
    }
    return ans;
}
bool check(int a,int b,int c){//保证可以构成大正方形
    if((a & 1) && (b & 2) && (c & 4))
        return true;
    if((a & 1) && (c & 2) && (b & 4))
        return true;
    if((b & 1) && (a & 2) && (c & 4))
        return true;
    if((b & 1) && (c & 2) && (a & 4))
        return true;
    if((c & 1) && (a & 2) && (b & 4))
        return true;
    if((c & 1) && (b & 2) && (a & 4))
        return true;
    return false;
}
int gcd(int a,int b){
    if(b == 0)
        return a;
    return gcd(b,a%b);
}
int cnt[10],use[10];
int fac[maxn];
int main(){
    //预处理因子个数
    for(int i = 1; i < maxn; i++){
        for(int j = i; j < maxn; j += i){
            fac[j]++;
        }
    }
    int t,x,y,z;
    ll ans;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&x,&y,&z);
        int xy = gcd(x,y);
        int yz = gcd(y,z);
        int xz = gcd(x,z);
        int xyz = gcd(xy,z);
        //容斥计算出每种状态的因子个数
        cnt[7] = fac[xyz];//111
        cnt[6] = fac[yz] - fac[xyz];//110
        cnt[5] = fac[xz] - fac[xyz];//101
        cnt[4] = fac[z] - fac[xz] - fac[yz] + fac[xyz];//100
        cnt[3] = fac[xy] - fac[xyz];//011
        cnt[2] = fac[y] - fac[xy] - fac[yz] + fac[xyz];//010
        cnt[1] = fac[x] - fac[xy] - fac[xz] + fac[xyz];//001
        ans = 0;
        for(int a = 1; a < 8; a++){
            for(int b = a; b < 8; b++){
                for(int c = b; c < 8; c++){
                    if(check(a,b,c)){
                        memset(use,0,sizeof(use));
                        use[a]++;
                        use[b]++;
                        use[c]++;//标记某种状态出现次数
                        ll tmp = 1;
                        for(int i = 1; i < 8; i++){//有使用重复组合数公式
                            if(use[i])
                                tmp *= C(cnt[i]+use[i]-1,use[i]);
                        }
                        if(tmp > 0)
                            ans += tmp;
                    }
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81146331