HDU 5072 容斥原理 + 质因数分解

来源:互联网 发布:java聊天室传文件 编辑:程序博客网 时间:2024/05/22 14:26

HDU 5072
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5072
题意:
给n个数,问有多少对数(a,b,c)使得gcd(a,b)=gcd(b,c)=gcd(a,c)=1,或者gcd(a,b),gcd(b,c),gcd(a,c)均不为1。
思路:
单求为1的倒还好办,因为数字都在1e5以内应该可以用一种质因数分解的方式求出来,但是第二个条件就不好求了。
所以问题转化一下,求tans = 不合法的对数,然后ans = 总数 - tans。
举了几个例子发现,对于每个数求出与他互质的数的个数u,剩下的就是不与他互质的数(n-1-u),则tans = sigma(u*(n-1-u)) / 2。证明的话大概用容斥原理。
然而怎么求与他互质的数的个数……然后就卡死了。
从网上搜解题报告,还是利用容斥原理来求。
对于一个数,质因数分解得到它的质因数数组ele[],然后每个ele最多使用一次能构成的倍数,给这些数的计数器+1,表示此数不与这些数互质。
在求与一个数互质的数dp时,进行同样的质因数分解得到质因数数组ele[],每个ele最多使用一次构成倍数temp。如果ele使用了奇数个,dp+=num[temp](num是计数器,temp表示这个倍数),否则dp-=num[temp]。具体可以用全概率公式的原理来解释。
源码:

#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <iostream>#include <string>using namespace std;#define LL long longconst int MAXN = 200000 + 5;int vis[MAXN];int g[MAXN];int num[MAXN];int p[MAXN], cnt;int data[MAXN];void init(){    memset(vis, 0, sizeof(vis));    cnt = 0;    for(int i = 2 ; i < MAXN ; i++){        if(vis[i] == 0){            p[cnt++] = i;            int u = i;            while(u < MAXN) vis[u] = 1, u += i;        }    }}int ele[MAXN];int main(){//    freopen("C.in", "r", stdin);    init();    int t;    scanf("%d", &t);    while(t--){        int n;        scanf("%d", &n);        memset(g, 0, sizeof(g));        for(int i = 0 ; i < n ; i++)            scanf("%d", &data[i]), g[data[i]] = 1;        memset(num, 0, sizeof(num));        for(int i = 0 ; i < n ; i++){            cnt = 0;            int temp = data[i];            for(int j = 0 ; p[j] * p[j] <= temp ; j++){                if(temp % p[j] == 0){                    ele[cnt++] = p[j];//                    printf("cnt = %d, p[j] = %d, temp = %d\n", cnt, p[j], temp);                    while(temp % p[j] == 0){                        temp /= p[j];//                        printf("temp = %d, p[j] = %d\n", temp, p[j]);                    }                }            }            if(temp != 1)   ele[cnt++] = temp;            for(int j = 0 ; j < (1 << cnt) ; j++){                int which = 1;                for(int k = 0 ; k < cnt ; k ++){                    if((1 << k) & j)                        which *= ele[k];                }                num[which]++;            }        }//        printf("second\n");        LL ans = 0;        for(int i = 0 ; i < n ; i++){            cnt = 0;            int temp = data[i];            for(int j = 0 ; p[j] * p[j] <= temp ; j++){                if(temp % p[j] == 0){                    ele[cnt++] = p[j];                    while(temp % p[j] == 0) temp /= p[j];                }            }            if(temp != 1)   ele[cnt++] = temp;            LL tans = 0;            for(int j = 1 ; j < (1 << cnt) ; j++){                int sta = 0;                LL which = 1;                for(int k = 0 ; k < cnt ; k++){                    if((1 << k) & j){                        sta++,  which *= ele[k];                    }                }                if(which > MAXN)    continue;                if(sta & 1) tans += num[which];                else    tans -= num[which];            }            if(tans > 0)    tans--;            ans += tans * (n - 1 - tans);        }        ans = (LL)n * (n - 1) * (n - 2) / 6 - ans / 2;        printf("%I64d\n", ans);    }    return 0;}
0 0
原创粉丝点击