链接
http://codeforces.com/contest/1008/problem/D
大意
给你 (A,B,C) ( A , B , C ) ,求无序三元组 (a,b,c) ( a , b , c ) 使得 a|A,b|B,c|C a | A , b | B , c | C ,一共 T T 组数据,
题解
官方题解里一堆长难句和生词,我看不懂
QwQ
Q
w
Q
在网上看到一个清奇的思路,分享一下
直接容斥的话很麻烦,一不小心可能就会
WA
W
A
掉了,传统的容斥思路实质上是强制规定了第一个位置填
A
A
的约数,第二个位置强行填的约数,第三个位置强行填
C
C
的约数,这样带来的问题就是一个数的约数可能也是另一个数的约数,再考虑上约数和约数可能相等,这个问题就变得异常复杂,其实是因为三个位置填的数之间的联系太复杂。
如果能按照某种划分标准,把所有的约数划分成三个集合,使得我从每个集合中取出一个数就能满足这三个数分别是的约数,而且集合与集合之间没有交集,那么这个问题就一下子简单很多。
我用
1...7
1...7
的二进制表示状态,第一位为
1
1
说明它是的约数,第二位为
1
1
说明它是的约数,第三位为
1
1
说明它是的约数
显然这
7
7
个集合任意选出几个数字,都不会有重复
我只要枚举集合编号,然后
check
c
h
e
c
k
一下我选出来的这些数能不能和
A,B,C
A
,
B
,
C
一一对应起来,如果能对应起来,我就把
card(i)×card(j)×card(k)
c
a
r
d
(
i
)
×
c
a
r
d
(
j
)
×
c
a
r
d
(
k
)
计入答案,如果
i=j
i
=
j
,就把
C2card(i)+2−1×card(k)
C
c
a
r
d
(
i
)
+
2
−
1
2
×
c
a
r
d
(
k
)
计入答案,如果
i=j=k
i
=
j
=
k
我就把
C3card(i)+3−1
C
c
a
r
d
(
i
)
+
3
−
1
3
,其它情况也是一样。
其中
card()
c
a
r
d
(
)
表示有限集合的元素数量
Crn+r−1
C
n
+
r
−
1
r
是从
n
n
个元素里允许重复地选择个元素的方案数
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
#define clear(x) memset(x,0,sizeof(x))
#define maxn 100010
using namespace std;
ll cnt[8], T, d[maxn], now[4];
bool ok[8][8][8];
ll read(ll x=0)
{
char f=1, c;
for(c=getchar();c<48 or c>57;c=getchar())if(c=='-')f=-1;
for(;c>=48 and c<=57;c=getchar())x=(x<<1)+(x<<3)+c-48;
return x;
}
ll C(ll n, ll m)
{
if(n==0)return 0;
ll ans=1, i;
for(i=n;i>n-m;i--)ans*=i;
for(i=1;i<=m;i++)ans/=i;
return ans;
}
void calc_d()
{
ll i, j;
for(i=1;i<maxn;i++)for(j=i;j<maxn;j+=i)d[j]++;
}
bool dfs(ll pos)
{
ll i, t;
if(pos>4)return true;
for(i=1;i<=3;i++)if(pos&now[i])
{
t=now[i], now[i]=0;
if(dfs(pos<<1))return true;
now[i]=t;
}
return false;
}
void calc_ok()
{
ll i, j, k;
for(i=1;i<8;i++)for(j=i;j<8;j++)for(k=j;k<8;k++)
{
now[1]=i, now[2]=j, now[3]=k;
ok[i][j][k]=dfs(1);
}
}
ll gcd(ll a, ll b){return !b?a:gcd(b,a%b);}
void calc_cnt(ll a, ll b, ll c)
{
clear(cnt);
ll ab=gcd(a,b), ac=gcd(a,c), bc=gcd(b,c), abc=gcd(ab,c);
cnt[1]=d[c]-d[ac]-d[bc]+d[abc]; //001
cnt[2]=d[b]-d[ab]-d[bc]+d[abc]; //010
cnt[3]=d[bc]-d[abc]; //011
cnt[4]=d[a]-d[ab]-d[ac]+d[abc]; //100
cnt[5]=d[ac]-d[abc]; //101
cnt[6]=d[ab]-d[abc]; //110
cnt[7]=d[abc]; //111
}
void solve(ll a, ll b, ll c)
{
ll i, j, k, ans=0;
calc_cnt(a,b,c);
for(i=1;i<8;i++)for(j=i;j<8;j++)for(k=j;k<8;k++)if(ok[i][j][k])
{
if(i==j and j==k)ans+=C(cnt[i]+2,3);
else if(i==j and j!=k)ans+=C(cnt[i]+1,2)*C(cnt[k],1);
else if(i!=j and j==k)ans+=C(cnt[i],1)*C(cnt[j]+1,2);
else ans+=C(cnt[i],1)*C(cnt[j],1)*C(cnt[k],1);
}
printf("%I64d\n",ans);
}
int main()
{
ll a, b, c;
calc_d();
calc_ok();
for(T=read();T;T--)
{
a=read(), b=read(), c=read();
solve(a,b,c);
}
return 0;
}