题意:
在 A,B,C A , B , C 的大长方体中有多少中小长方体满足小长方体的三条边可以分别被大的三条边整除
解析:
在假设没有重复计算的时候,答案应该是 F(A)∗F(B)∗F(C) F ( A ) ∗ F ( B ) ∗ F ( C ) ( F(x) F ( x ) 表示 x x 的因子的个数)
所以我们只要排除掉这些重复计算的方案数就是答案
如果可能重复计算的话,说明至少有两个数有两个以上的相同因子 (因为只有一个是不会多算的,多算的情况差不多是123和213)
两个数重复的情况:
- 在 gcd(A,B) g c d ( A , B ) 的因子中任意选择两个和c的组合便会多计算一次 所以减去 C2F(gcd(A,B)∗F(C) C F ( g c d ( A , B ) 2 ∗ F ( C )
- gcd(B,C) g c d ( B , C ) 和 gcd(A,C) g c d ( A , C ) 同理 减去 C2F(gcd(B,C)∗F(A)+C2F(gcd(A,C)∗F(B) C F ( g c d ( B , C ) 2 ∗ F ( A ) + C F ( g c d ( A , C ) 2 ∗ F ( B )
三个数重复 (a,b,c|gcd(A,B,C)) ( a , b , c | g c d ( A , B , C ) ) 的情况:
a=b≠c a = b ≠ c 时
(1,2,2)(2,1,2)(2,2,1)(1,1,2)(1,2,1)(2,1,1)这些是我们一开始时算入的,但是在上面两个数重复的情况下把全部都剪完了,多减去了两个(1,1,2)(1,2,2),所以我们要加上 2∗C2F(gcd(A,B,C)) 2 ∗ C F ( g c d ( A , B , C ) ) 2
a≠b≠c a ≠ b ≠ c 时
(1,2,3)(1,3,2)(2,1,3)(2,3,1)(3,1,2)(3,2,1),我们在gcd(A,B)时减去的有(1,2,3)(2,3,1)(1,3,2),同理gcd(B,C)gcd(A,C)各减去三个,多减了3次再加上(1,2,3),所以要加上4个即加上 4∗C3F(gcd(A,B,C)) 4 ∗ C F ( g c d ( A , B , C ) ) 3
只一个数非一个数因子 (a|gcd(A,B,C),b|gcd(A,B,C),c|gcd(A,B)) ( a | g c d ( A , B , C ) , b | g c d ( A , B , C ) , c | g c d ( A , B ) ) 的情况
A=4,B=4,C=6 (1,2,4)
- 开始时记下(1,4,2)(2,4,1)(4,1,2)(4,2,1)
- gcd(A,B)时减去(1,4,2)(2,4,1)
- gcd(B,C)时减去(4,1,2)
- gcd(A,C)时减去(1,4,2)
减完了,所以还要加上去,从gcd(A,B)和gcd(A,B,C)的不同的因子中选一个加上gcd(A,B,C)中选两个即加上 (F(gcd(A,B))−F(gcd(A,B,C)))∗C2F(gcd(A,B,C)) ( F ( g c d ( A , B ) ) − F ( g c d ( A , B , C ) ) ) ∗ C F ( g c d ( A , B , C ) ) 2
b|gcd(A,C),a|gcd(B,C)同,加上 (F(gcd(A,C))−F(gcd(A,B,C)))∗C2F(gcd(A,B,C)),(F(gcd(B,C))−F(gcd(A,B,C)))∗C2F(gcd(A,B,C)) ( F ( g c d ( A , C ) ) − F ( g c d ( A , B , C ) ) ) ∗ C F ( g c d ( A , B , C ) ) 2 , ( F ( g c d ( B , C ) ) − F ( g c d ( A , B , C ) ) ) ∗ C F ( g c d ( A , B , C ) ) 2
每个数皆非一个数因子(a|gcd(A,B),b|gcd(B,C),c|gcd(C,A))的情况
A=6,B=10,C=15 (2,5,3)
- 开始时记下(2,5,3)(3,2,5),但在考虑两个数重复的时候都没有减去
- 所以要额外 减去
(F(gcd(A,B))−F(gcd(A,B,C)))∗(F(gcd(A,C))−F(gcd(A,B,C)))∗(F(gcd(B,C))−F(gcd(A,B,C))) ( F ( g c d ( A , B ) ) − F ( g c d ( A , B , C ) ) ) ∗ ( F ( g c d ( A , C ) ) − F ( g c d ( A , B , C ) ) ) ∗ ( F ( g c d ( B , C ) ) − F ( g c d ( A , B , C ) ) )
总结:
设 : A=a , B=b , C=c , gcd(A,B,C)=abc , gcd(A,B)=ab , gcd(B,C)=bc , gcd(A,C)=ac
ans=F(a)*F(b)*F(c)-C(F(ab),2)*F(c)-C(F(ac),2)*F(b)-C(F(bc),2)*F(a)
ans+=2*C(F(abc),2)+4*C(F(abc),3)
ans+=(F(ab)+F(bc)+F(ac)-3*F(abc))*C(F(abc),2)
ans-=(F(ab)-F(abc)) * (F(ac)-F(abc)) * (F(bc)-F(abc))
预处理每个数的因子的个数
这算是一个比较常用的知识了吧,而巧合的是这个东西的做法和这题的做法很像 |
n=pq11∗pq22...pqkk n = p 1 q 1 ∗ p 2 q 2 . . . p k q k
n的因子数为 (q1+1)∗(q2+1)∗...∗(qk+1) ( q 1 + 1 ) ∗ ( q 2 + 1 ) ∗ . . . ∗ ( q k + 1 )
求A的因子的个数相当于求A的两个互质因子的因子个数之积
- F(18)=F(2)∗F(9),相当于(1,2)里面选一个乘上(1,3,9)里面选一个 F ( 18 ) = F ( 2 ) ∗ F ( 9 ) , 相 当 于 ( 1 , 2 ) 里 面 选 一 个 乘 上 ( 1 , 3 , 9 ) 里 面 选 一 个
在非互质的情况下,会出现重复
- F(3)=2(1,3),两个三的组合有(1,1),(1,3),(3,1),(3,3),而F(9)=3(1,3,9) F ( 3 ) = 2 ( 1 , 3 ) , 两 个 三 的 组 合 有 ( 1 , 1 ) , ( 1 , 3 ) , ( 3 , 1 ) , ( 3 , 3 ) , 而 F ( 9 ) = 3 ( 1 , 3 , 9 )
现在考虑的就是去重的情况
设:求解数 ⇒ ⇒ A A , 的一个质因数 ⇒ ⇒ a a , 有 (b与a互质) ( b 与 a 互 质 )
显然, F(A)=(p+1)∗F(b) F ( A ) = ( p + 1 ) ∗ F ( b )
而 F(A∗a)=(p+2)∗F(b) F ( A ∗ a ) = ( p + 2 ) ∗ F ( b )
所以,对于任意 A∗a A ∗ a , 我们只需要知道 A A 对于的次方数 p p , 就可以通过的因子数转换过来
实现起来其实也不难,我们把这个过程塞进素数筛里面,为了统一,我们对于任何数的转换只考虑它的第一个质因子 3→92→6 3 → 9 2 → 6 ,在素数筛的时候记录下每个数的第一个质因子的次方数即可
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100000;
ll f[N+9],p[N+9];
///
bool notpri[N+9];
int pri[N+9>>1],now;
void init(){
now=0;f[1]=1;
notpri[0]=notpri[1]=1;
for(int i=2;i<=N;i++){
if(!notpri[i])pri[++now]=i,f[i]=2,p[i]=1;
for(int j=1;j<=now&&pri[j]*i<=N;j++){
notpri[i*pri[j]]=1;
if(i%pri[j]==0){//有pri[j]作为质因子时
f[i*pri[j]]=f[i]*(p[i]+2)/(p[i]+1);
p[i*pri[j]]=p[i]+1ll;
break;
}
f[i*pri[j]]=f[i]*f[pri[j]];//互质则直接乘
p[i*pri[j]]=1;
}
}
}
int main(){
init();
int t;scanf("%d",&t);
while(t--){
ll a,b,c;scanf("%lld%lld%lld",&a,&b,&c);
ll ab=__gcd(a,b),bc=__gcd(b,c),ac=__gcd(a,c);
ll abc=__gcd(ab,c);
ll ans=f[a]*f[b]*f[c];
ans-=(f[ab]*(f[ab]-1ll))/2*f[c];
ans-=(f[ac]*(f[ac]-1ll))/2*f[b];
ans-=(f[bc]*(f[bc]-1ll))/2*f[a];
ans+=f[abc]*(f[abc]-1ll)+(f[abc]*(f[abc]-1ll)*(f[abc]-2ll))*2/3;
ans+=(f[ab]+f[bc]+f[ac]-3ll*f[abc])*(f[abc]*(f[abc]-1ll))/2;
ans-=(f[ab]-f[abc])*(f[ac]-f[abc])*(f[bc]-f[abc]);
printf("%lld\n",ans);
}
}