Description
Input
Output
Sample Input
4 2 3 4 5 4 2 4 6 8 7 2 3 4 5 7 6 8
Sample Output
1 0 34
题意:给定n个数,从n个数找出四个数,使这四个数的最大公约数为1,找出有多少对这样的组合。
题解:四个数的公约数为1,并不代表四个数两两互质。比如(2,3,4,5)公约数为1,但是
2和4并不互质。从反面考虑,先求出四个数公约数不为1的情况个数,用总的方案个数
减去四个数公约数不为1的情况个数就是所求。
求四个数公约数不为1的情况个数,需要将N个数每个数质因数分解,纪录下所有不同
的素因子所能组成的因子(就是4个数的公约数),并统计构成每种因子的素因子个数,
和因子总数。然后再计算组合数。比如说因子2的个数为a,则四个数公约数为2的个数
为C(a,4),因子3的个数为b,则四个数公约数为3的个数为C(b,4),因子6(2*3)的个
数为c,则四个数公约数的个数为C(c,4)。
但是公约数为2的情况中或者公约数为3的情况中可能包括公约数为6的情况,相当于几
个集合求并集,这就需要容斥定理来做。
容斥原理应用,以2为因子的数有a个,3为因子 的数有b个,6为因子的数有c个,
n个数不互质的四元组个数为C(a,4)+C(b,4)-C(c,4) (含奇数个素因子的加,偶数个素因子的减),
下面就是统计出2,3,5这些因子的倍数的个数,对C(a,4)容斥!
代码:弄清思路以后就很好做了,一环扣一环,用二进制进行枚举,很棒!
#include <iostream> #include <cstring> #include <cmath> #include <cstdio> using namespace std; const int maxn=1e4+5; typedef long long ll; //因为c(5000,4)=26010428123750。所以要用 long long 能long long 就 long long ll c(ll n) { return n*(n-1)*(n-2)*(n-3)/24; } ll prime[20],cnt=0,num[maxn][2]; //0代表数目 1代表奇偶性 void solve(ll n) { //memset(prime,0,sizeof(prime)); //这句话和cin同时用会超时 所以涉及到复杂度全用scanf cnt=0; for(int i=2;i<=sqrt(n);i++) { if(n%i==0) { prime[cnt++]=i; while(n%i==0) n/=i; } } if(n!=1) prime[cnt++]=n; //这里一定注意是n不是i for(int i=1;i<(1<<cnt);i++) //i=0无意义 num[1][] 无意义 { ll flag=0,tmp=1; for(int j=0;j<cnt;j++) { if(i&(1<<j)) { flag++; tmp*=prime[j]; } } //其实这里可以优化一下 若大于2500的数作为因子 他的倍数不可能够四个的 //不过是否优化对时间无影响 num[tmp][0]++; //数目 num[tmp][1]=flag;//奇偶性 } } int main() { ll n,data; while(scanf("%lld",&n)!=EOF) //是lld { memset(num,0,sizeof(num)); for(int i=0;i<n;i++) { scanf("%lld",&data); solve(data); } ll ans=c(n); for(int i=2;i<=maxn/4;i++) { if(num[i][0]) //0代表数目 { if(num[i][1]&1) //1代表flag奇偶性 ans-=c(num[i][0]); //注意这里用的是数目 //不是num[i][1] 更不是num[1]什么 是num[i][0] else ans+=c(num[i][0]); } } printf("%lld\n",ans); } return 0; }