poj3904 Sky Code —— 唯一分解定理 + 容斥原理 + 组合

来源:互联网 发布:中考倒计时软件下载 编辑:程序博客网 时间:2024/05/18 00:27

题目链接:http://poj.org/problem?id=3904

类似题目poj2773(题解):http://blog.csdn.net/DOLFAMINGO/article/details/72669432


题解:

给出n个数, 随便挑4个, 使得这四个数的最大公约数为1, 问有多少种组合?

思路:先用容斥原理计算出四个数的最大公约数>=1的组合数, 然后再用总数C(n,4)减之。


1.将每个数进行分解质因数, 然后再根据这些质因数组合出不同的因子,并记录这个因子出现的次数以及由多少个质因数构成。

2.容斥原理:比如因子2的个数为a,则四个数公约数为2的个数 为C(a,4),因子3的个数为b,则四个数公约数为3的个数为C(b,4),因子6(2*3)的个 数为c,则四个数公约数的个数为C(c,4)。 但是公约数为2的情况中或者公约数为3的情况中可能包括公约数为6的情况,相当于几个集合求并集,这就需要容斥定理来做。

3.如果这个因子出现的次数>=4, 则表明这个因子可以作为某四个数的最大公约数的因子。

4.根据容斥原理:当这个因子的由奇数个质因数构成时, 加; 当这个因子由偶数个质因子构成时, 减。

5. ans = C(n,4) - gcd(a,b,c,d)!=1的组合数。



代码如下:

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <string>#include <vector>#include <map>#include <set>#include <queue>#include <stack>#include <sstream>#include <algorithm>using namespace std;#define pb push_back#define mp make_pair#define ms(a, b)  memset((a), (b), sizeof(a))#define eps 0.0000001typedef long long LL;const int INF = 2e9;const LL LNF = 9e18;const int mod = 1e9+7;const int maxn = 1e4+10;LL pri[maxn], fac_num[maxn], fac_pri[maxn];LL n, cnt;LL C(LL x){    return x*(x-1)*(x-2)*(x-3)/24;}void Divide(LL x){    cnt = 0;    for(int i = 2; i*i<=x; i++)    {        if(x%i==0)        {            pri[cnt++] = i;            while(x%i==0)  x /= i;        }    }    if(x!=1) pri[cnt++] = x;}void Unit(){    for(LL s = 1; s < (1<<cnt); s++)   //用二进制, 亦可用递归    {        LL tmp = 1, sum = 0;        for(int j = 0; j<cnt; j++)        if(s&(1<<j))        {            tmp *= pri[j];            sum++;        }        fac_num[tmp]++;        fac_pri[tmp] = sum;    }}void init(){    ms(fac_num, 0);    ms(fac_pri, 0);    LL x;    for(int i = 1; i<=n; i++)    {        scanf("%lld",&x);        Divide(x);  //分解质因数        Unit();     //质因数可以组成哪些因子(这些因子就是四个数的约数)    }}void solve(){    LL tmp = 0;    for(int i = 1; i<=1e4; i++) //容斥, 计算gcd(a,b,c,d)!=1的个数    {        if(fac_num[i]>=4)   //这个因子的个数必须不小于4, 才能成为4个数的约束        {            if(fac_pri[i]&1)    //素数个数为奇数时, 加                tmp += C(fac_num[i]);            else                //素数个数为偶数时, 减                tmp -= C(fac_num[i]);        }    }    LL ans = C(n) - tmp;    //总的减去gcd(a,b,c,d)!=1的个数,即为gcd(a,b,c,d)=1的个数。    printf("%lld\n", ans);}int main(){    while(scanf("%lld",&n)!=EOF)    {        init();        solve();    }}


原创粉丝点击