poj 3904
题意:给出n个数,问有多少组(a,b,c,d)公约数为1。
题解:有两种思路,一个是mobius反演,一种是容斥。
先介绍一下容斥的思路:我们考虑该问题的逆问题,求多少组数的gcd>=2,那么集合gcd为质数的交集就是我们要答案咯。枚举出每个素数乘积的因子数字,然后容斥筛一遍就可以了
(2017.9.2 这里补充一下,关于为什么用包含某个素因子个数的并集可以表示所有解。有点类似数筛的思想。比如,我们计算包含2这个因子的个数的时候,是不是把包含2,4,6,8等因子的结果一并算进去了,3的时候也是一样,把,3,6,9。这个过程和埃氏筛法的思想类似,一定可以包含所有的情况,然后其并集就是我们要的答案)
#include<stdio.h> #include<string.h> using namespace std; #define LL long long #define maxn 10005 LL node[maxn],num[maxn],vist[maxn],prime[maxn]; void Init() { LL i; for(i=4;i<maxn;i++) node[i]=i*(i-1)*(i-2)*(i-3)/24; } void make_count(int m) { int i,j,tmp,flag,cnt=0; for(i=2;i*i<=m;i++) if(m&&m%i==0) { prime[cnt++]=i; while(m&&m%i==0) m/=i; } if(m>1) prime[cnt++]=m; for(i=1;i<(1<<cnt);i++) { tmp=1,flag=0; for(j=0;j<cnt;j++) if(i&(1<<j)) flag++,tmp*=prime[j]; num[tmp]++; //统计当前因子出现的次数 vist[tmp]=flag; //记录当前因子是由多少个素因子组成,奇加偶减 } } int main() { Init(); int n,i,x; while(~scanf("%d",&n)) { memset(num,0,sizeof(num)); memset(vist,0,sizeof(vist)); for(i=0;i<n;i++) { scanf("%d",&x); make_count(x); } LL ans=0; for(i=1;i<maxn;i++) if(num[i]) { if(vist[i]&1) ans+=node[num[i]]; else ans-=node[num[i]]; } printf("%I64d\n",node[n]-ans); } return 0; }
mobius:常见的为求gcd()==k 或者gcd为质数的情况
我们令
f(x)=四个数的gcd是x的情况数 ;
F(x)=四个数的gcd是x的倍数的情况;
这里要求的n为1。
#include <iostream> #include <cstring> #include <cstdio> using namespace std; typedef long long ll; ll num[10001]; ll mu[10001]; ll c[10001]; ll vis[10001]; ll prime[10001]; void init() { for(ll i=1;i<=10000;i++) { c[i]=i*(i-1)*(i-2)*(i-3)/24; } memset(mu,0,sizeof(mu)); memset(vis,0,sizeof(vis)); memset(prime,0,sizeof(prime)); int ret=0; mu[1]=1; for(int i=2;i<=10000;i++) { if(!vis[i]) { mu[i]=-1; prime[ret++]=i; } for(int j=0;j<ret &&i*prime[j]<=10000;j++) { ll temp=i*prime[j]; vis[temp]=1; if(i%prime[j]) { mu[temp]=-mu[i]; } else break; } } } void get(ll x) { for(ll i=1;i*i<=x;i++) { if(x%i==0) { num[i]++; if(i*i!=x) num[x/i]++;// attention// } } } int main() { int n; init();// while(~scanf("%d",&n)) { ll x; memset(num,0,sizeof(num)); for(int i=1;i<=n;i++) { scanf("%lld",&x); get(x); } ll ans=0; for(int i=1;i<=10000;i++) { if(mu[i]==0 || num[i]<4) continue; ans+=mu[i]*c[num[i]]; } printf("%lld\n",ans); } return 0; }