SCU - 4573 和 POJ 3904 Sky Code 【思维 + 容斥定理 or 反演】

版权声明:本文为博主原创文章,喜欢就点个赞吧 https://blog.csdn.net/Anxdada/article/details/80022300

传送门
题意: 就是给定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);
}

没有更多推荐了,返回首页