hdu 6053: TrickGCD (2017 多校第二场 1009) 【莫比乌斯 容斥原理】


题目链接

定义f[n]表示n是最大公约数情况下的计数,F[n]为n是公约数情况下的计数

(可以和 http://www.cnblogs.com/Just--Do--It/p/7197788.html hdu1695 进行类比)

显然F[n]和f[n]是满足下面这个关系的

所以,可以用下面这个公式求解f[n]

  

得到下面的AC代码

 

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

#define max(a,b) ((a)>(b)? (a):(b))
#define min(a,b) ((a)<(b)? (a):(b))

const int maxn=1e5+7;
int prime[maxn+5];
bool check[maxn+5];
int mu[maxn+5];

void init()
{
    mu[1]=1;
    int tot=0;
    for(int i=2;i<=maxn;i++)
    {
        if(!check[i])
        {
            prime[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot;j++)
        {
            if(i*prime[j]>maxn) break;
            check[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            else
            {
                mu[i*prime[j]]=-mu[i];
            }
        }
    }
}

const int N=1e5+5;
const int mod=1e9+7;

int n;
int num[2*N];
LL F[N];
LL f[N];


LL qpow(LL x,LL n)
{
    LL ret=1;
    for(;n;n>>=1)
    {
        if(n&1) ret=ret*x%mod;
        x=x*x%mod; 
    }
    return ret;
}

int main()
{
    init();
    int T; 
    scanf("%d",&T);
    for(int kase=1;kase<=T;kase++)
    {
        memset(num,0,sizeof(num));
        memset(f,0,sizeof(f));
        int max_gcd=1e9,max_a=0;
        LL ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            int t;
            scanf("%d",&t);
            num[t]++;
            max_gcd=min(t,max_gcd);
            max_a  =max(t,max_a  );
        }
        for(int i=1;i<2*N;i++)
            num[i]+=num[i-1];
        for(int i=2;i<=max_gcd;i++)
        {
            F[i]=1;
            for(int j=i;j<=max_a;j+=i)
                F[i]=F[i]*qpow(j/i,num[j+i-1]-num[j-1])%mod;
        }
//        =================================
        for(int i=2;i<=max_gcd;i++)
            for(int j=1;i*j<=max_gcd;j++)
                f[i]=(f[i]+mu[j]*F[i*j])%mod;
        for(int i=2;i<=max_gcd;i++)
            ans=(ans+f[i])%mod;
//        =================================
        printf("Case #%d: %lld\n",kase,ans); 
    }
}

 

然而!=====所夹的部分可以用一行代码代替!!!!虽然运行时间不会减少多少,不过代码量上优化了很多!

不过这种写法的实质其实可以从容斥原理的角度来考虑,再借用了莫比乌斯函数的性质。

定义:  性质Pi表示i是对象x的一个质因数,  集合Ai表示具有性质Pi的对象的集合

比较容易想到,所有可能的公因数对应的对象计数之和,恰为所有集合的并 中的对象的总个数

那么用容斥原理求 所有集合的并 中的对象的总个数时,奇数个基本集合的交前面的系数是正一,偶数个的是负一。并且,n的因数中包含质因数平方的F[n]在这里面是不需要被计数的。

这样恰好就与莫比乌斯函数的性质产生了联系。

参考博客:  http://blog.csdn.net/acterminate/article/details/76216345

也就变成了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

#define max(a,b) ((a)>(b)? (a):(b))
#define min(a,b) ((a)<(b)? (a):(b))

const int maxn=1e5+7;
int prime[maxn+5];
bool check[maxn+5];
int mu[maxn+5];

void init()
{
    mu[1]=1;
    int tot=0;
    for(int i=2;i<=maxn;i++)
    {
        if(!check[i])
        {
            prime[tot++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<tot;j++)
        {
            if(i*prime[j]>maxn) break;
            check[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            else
            {
                mu[i*prime[j]]=-mu[i];
            }
        }
    }
}

const int N=1e5+5;
const int mod=1e9+7;

int n;
int num[2*N];
LL F[N];
LL f[N];


LL qpow(LL x,LL n)
{
    LL ret=1;
    for(;n;n>>=1)
    {
        if(n&1) ret=ret*x%mod;
        x=x*x%mod; 
    }
    return ret;
}

int main()
{
    init();
    int T; 
    scanf("%d",&T);
    for(int kase=1;kase<=T;kase++)
    {
        memset(num,0,sizeof(num));
        memset(f,0,sizeof(f));
        int max_gcd=1e9,max_a=0;
        LL ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            int t;
            scanf("%d",&t);
            num[t]++;
            max_gcd=min(t,max_gcd);
            max_a  =max(t,max_a  );
        }
        for(int i=1;i<2*N;i++)
            num[i]+=num[i-1];
        for(int i=2;i<=max_gcd;i++)
        {
            F[i]=1;
            for(int j=i;j<=max_a;j+=i)
                F[i]=F[i]*qpow(j/i,num[j+i-1]-num[j-1])%mod;
//            printf("F[%d]=%4lld\n",i,F[i]);
            ans=(ans-mu[i]*F[i]+mod)%mod;
        }
        printf("Case #%d: %lld\n",kase,ans); 
    }
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM