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