传送门
题意: 就是给定n个数, 问这里面有多少个四元组满足gcd(a, b, c, d) == 1.
思路: 因为题意的那个条件并不是说一定这四个元素就是两两互质的, 所以算正面是有点难算的, 所以我们考虑反面, 用C(n, 4) - 那些gcd(四元组) != 1的, 这是我们就需要枚举gcd了, 1 - 1e4, 那么我们有什么方法可以更快的得到gcd可以等于2的有多少个数乐, 此时我们就需要分解每一个数的质因子, 把每一个数的质因子可能组成的所有数算出来, 同时记录这个数出现的次数, 如果是素数的幂的数可以不用考虑. 因为已经考虑过了这个素数了. 然后就是一个数可能会被多次计算, 所以需要容斥一下. 比如2, 3, 6的分别有a, b, c个人, 那么我们算的是否明显是C(a, 4) + C(b, 4) - C(c, 4), 就是因为6之前已经被2, 3算过一次了, 所以需要减去多余算的一次, 这里就用的容斥…… 即奇加偶减 , 这里的奇偶是对于一个数的素因子个数而言的.
AC Code
const int maxn = 1e4+5;
int vis[maxn], num[maxn];
vector<int>ve;
ll s[maxn];
void init() {
for (ll i = 4 ; i <= 10000 ; i ++) {
s[i] = i*(i-1)*(i-2)*(i-3)/24;
}
}
void cal(int x) {
ve.clear();
for (int i = 2 ; i * i <= x ; i ++) {
if (x % i == 0) {
ve.pb(i);
while(x % i == 0) x /= i;
}
}
if (x != 1) ve.pb(x); int si = sz(ve);
for (int i = 1 ; i < (1<<si) ; i ++) {
ll cnt = 0, tmp = 1;
for (int j = 0 ; j < si ; j ++) {
if (i & (1<<j)) {
++cnt ;
tmp *= ve[j];
}
}
vis[tmp]++;
num[tmp] = cnt;
}
}
void solve()
{
int n; Fill(vis, 0); Fill(num, 0);
scanf("%d", &n);
for (int i = 1 ; i <= n ; i ++) {
int x; scanf("%d", &x);
cal(x);
}
ll ans = 0;
for (int i = 1 ; i <= 10000 ; i ++) {
if (!vis[i]) continue;
if (num[i] & 1) ans += s[vis[i]];
else ans -= s[vis[i]];
}
printf("%lld\n", s[n] - ans);
}