ACdream 1114 Number theory 莫比烏斯反演 或 容斥原理


題意:給n個數,n 和 每個數的範圍都是 1---222222,求n個數中互質的對數。

題解:

f(d) 表示gcd爲d的倍數的數的對數

g(d)表示gcd恰好爲d的數的對數

f(d) = ∑ g(n) (n % d == 0)

g(d) = ∑  mu[n / d] * f(n) (n %d == 0)

上面兩個式子就是套路,沒啥難的

所以 g(1) = ∑mu[n] * f(n)

求一下mu【】,枚舉一下n就可以AC了

篩法求下每個數的倍數的個數。在莫比烏斯下就好了

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
#define N 222222

int mib[N+10];
int f[N+10];
bool vis[N+10];
void mobius()
{
	int i,j;
	for(i=1;i<=N;i++) mib[i]=1,vis[i]=false;
	for(i=2;i<=N;i++)
	{
		if(vis[i]) continue;
		for(j=i;j<=N;j+=i)
		{
			vis[j]=true;
			if((j/i)%i==0)
			{
				mib[j]=0;
				continue;
			}
			mib[j]=-mib[j];
		}
	}
	f[0]=mib[0];
	for(i=1;i<=N;i++)
		f[i]=f[i-1]+mib[i];
}
int p[N+10];
int cnt[N+10],num[N+10];
int main()
{
	mobius();
	int i,j,k;
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		memset(num,0,sizeof(num));
		memset(cnt,0,sizeof(cnt));
		int mx=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&p[i]);
			num[p[i]]++;
			mx=max(mx,p[i]);
		}
		for(i=1;i<=N;i++)
			for(j=i;j<=N;j+=i)
				cnt[i]+=num[j];
		ll ans=0;
		for(i=1;i<=mx;i++)
		{
			if(!cnt[i]) continue;
			ans+=(ll)mib[i]*((ll)(cnt[i])*(cnt[i]-1)/2);
		}
		cout<<ans<<endl;
	}
}


當時用容斥原理,各種打表優化,後來就過了。。還是莫比烏斯好啊。。

/*
* this code is made by 2Bpencil
* Problem: 1114
* Verdict: Accepted
* Submission Date: 2014-06-09 00:18:48
* Time: 756MS
* Memory: 62448KB
*/
#include<iostream> 
#include<stdio.h> 
#include<string.h> 
#include<algorithm> 
#include<math.h>
using namespace std; 
#define ll long long 
#define NN 222229 
   
int p[NN],g[NN]; 
int t[20]; 
ll h[NN]; 
#define N 500  //質數範圍    
int prime[1000];  //prime[0]=2,prime[1]=3,prime[2]=5,…… 
   
struct node
{
    int cn;
    int a[65];
}po[NN];
 
void init_prime()   
{   
    int i, j;   
    for(i = 2;i <= sqrt(N*1.0); ++i)   
    {   
        if(!prime[i])   
            for(j = i * i; j < N; j += i)   
                prime[j] = 1;   
    }   
    j = 0;   
    for(i = 2;i <= N; ++i)   
        if(!prime[i])    
            prime[j++] = i;   
}
void solve(int x) 
{ 
    int i,j,k,cnt=0;
    int he=x;
    for(i=0;prime[i]*prime[i]<=x;i++) 
    { 
        if(x%prime[i]==0) 
        { 
            t[cnt++]=prime[i]; 
            while(x%prime[i]==0) 
                x/=prime[i]; 
        } 
    } 
    if(x!=1) 
        t[cnt++]=x; 
    int tt=(1<<cnt); 
    po[he].cn=0;
    for(i=1;i<tt;i++) 
    { 
        int tsum=1,tflag=0; 
        for(j=0;j<cnt;j++) 
        { 
            if((i>>j)&1) 
            { 
                tsum*=t[j]; 
                tflag++; 
            } 
        } 
        po[he].a[po[he].cn++]=tsum;
        g[tsum]=tflag; 
    } 
} 
int main() 
{ 
    int n; 
    ll i,j;
    int k;
    h[0]=h[1]=0;
    init_prime();
    memset(g,0,sizeof(g)); 
    for(i=2;i<=222222;i++) 
    { 
        h[i]=i*(i-1)/2;
    } 
    for(i=1;i<=222222;i++)
        solve(i);
    while(scanf("%d",&n)!=EOF) 
    { 
        memset(p,0,sizeof(p)); 
        int mx=0;
        for(i=0;i<n;i++) 
        { 
            scanf("%d",&k); 
            for(j=0;j<po[k].cn;j++)
                p[po[k].a[j]]++;
            mx=max(k,mx);
        } 
        ll res=h[n]; 
        for(i=1;i<=mx;i++) 
        { 
            if(p[i]!=0) 
            { 
                if(g[i]&1) 
                    res-=h[p[i]]; 
                else 
                    res+=h[p[i]]; 
            } 
        } 
        printf("%lld\n",res);
    } 
}


發佈了212 篇原創文章 · 獲贊 3 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章