poj 3904 莫比乌斯反演 或 容斥原理

poj 3904 莫比乌斯反演 或 容斥原理
题目:
给出n个数字a1,a2,...an, 求从中选出一个四元组(a,b,c,d), 使得gcd(a,b,c,d)=1,求符合条件的四元组的数目。

限制:
1 <= n <= 1e4; 1 <= ai <= 1e4

思路:
莫比乌斯反演入门题
设f(k)为gcd(a,b,c,d)=k的四元组的数目,
设F(k)为gcd(a,b,c,d)为k的倍数的四元组的数目,
F(k)可以通过这个方式得到:
先通过对每个ai分解因数预处理处理出来,对于每个k,有多少个ai是它的倍数,假设为m,然后F(k)=C(m,4)。
令lim=max(a1,a2,...,an)
最后f(1)=mu[1]*F(1) + mu[2]*F(2) + ... + mu[lim]*F(lim)

/*poj 3904
  题目:
  给出n个数字a1,a2,...an, 求从中选出一个四元组(a,b,c,d), 使得gcd(a,b,c,d)=1,求符合条件的四元组的数目。
  限制:
  1 <= n <= 1e4; 1 <= ai <= 1e4
  思路:
  莫比乌斯反演入门题
  设f(k)为gcd(a,b,c,d)=k的四元组的数目,
  设F(k)为gcd(a,b,c,d)为k的倍数的四元组的数目,
  F(k)可以通过这个方式得到:
  先通过对每个ai分解因数预处理处理出来,对于每个k,有多少个ai是它的倍数,假设为m,然后F(k)=C(m,4)。
  令lim=max(a1,a2,...,an)
  最后f(1)=mu[1]*F(1) + mu[2]*F(2) + ... + mu[lim]*F(lim)
 */
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        
using namespace std;
#define LL __int64
const int N=100005;
int cnt[N];
int mu[N];
void getMu(){
	for(int i=1;i
        
        
          n-m;--i) ret*=(LL)i; for(int i=2;i<=m;++i) ret/=i; return ret; } LL gao(int x,int c){ LL ret=C(c,4); return mu[x]*ret; } int main(){ getMu(); int n,a,_max; while(scanf("%d",&n)!=EOF){ _max=0; memset(cnt,0,sizeof(cnt)); for(int i=0;i 
         
           =4) ans+=gao(i,cnt[i]); } printf("%I64d\n",ans); } return 0; } 
          
        
       
       
      
      
     
     
    
    


同样用容斥也可以做

#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        
using namespace std;
#define LL __int64
const int N=100005;
int cnt[N];
LL C(int n,int m){
	if(n
        
        
          n-m;--i) ret*=(LL)i; for(int i=2;i<=m;++i) ret/=i; return ret; } LL gao(int x,int c){ LL ret=C(c,4); int lim=sqrt(x); int flag=0; for(int i=2;i<=lim;++i){ if(x%i==0){ ++flag; x/=i; } if(x%i==0) return 0; } if(x!=1) ++flag; if(flag%2==0) return ret; else return -ret; } int main(){ int n,a,_max; while(scanf("%d",&n)!=EOF){ _max=0; memset(cnt,0,sizeof(cnt)); for(int i=0;i 
         
           =4) ans+=gao(i,cnt[i]); } printf("%I64d\n",ans); } return 0; } 
          
        
       
       
      
      
     
     
    
    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值