1. 程式人生 > >HDU 6053 TrickGCD(分塊)

HDU 6053 TrickGCD(分塊)

%d space 復雜 cstring 前綴 == str 結果 logs

【題目鏈接】 http://acm.hdu.edu.cn/showproblem.php?pid=6053

【題目大意】

  給出一個數列每個位置可以取到的最大值,
  問這個可以構造多少個數列,使得他們的最大公約數大於1

【題解】

  我們可以枚舉最大公約數k,對於k來說,
  他對答案的貢獻為∏[ai/k],我們將數列中的數字轉化為權值數組
  ∏_{i=1}^{100000}[i/k],對於求解i/k的部分我們可以進行數值分塊,
  j*k-1~j*k+k-1的數值除k得到的結果都是相同的,因此可以直接求這個結果的冪次,
  這時候只要再加一個權值數組的前綴和,問題就迎刃而解了。


  數值分塊計算的復雜度為n+n/2+n/3+n/4+n/5+……+n/n=nlogn。
  對於計算結果,我們需要進行容斥,奇數次素數乘的系數為1,偶數次素數乘的系數為-1,
  對於出現素數冪的合數其系數為0,
  我們發現這個容斥恰好是莫比烏斯函數的相反數,因此我們取反即可。
  這有個小小的優化,對於系數為0的情況,我們可以直接跳過,不進行計算。

【代碼】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=200010;
typedef long long LL;
const LL mod=1000000007;
int T,n,a[N],b[N],cnt[N],cas=1,p[N]; 
LL ans=0;
int tot,miu[N],sum[N],v[N];
void read(int&a){
    char ch;while(!((ch=getchar())>=‘0‘)&&(ch<=‘9‘));
    a=ch-‘0‘;while(((ch=getchar())>=‘0‘)&&(ch<=‘9‘))a*=10,a+=ch-‘0‘;
}
void mobius(int n){
    int i,j;
    for(miu[1]=1,i=2;i<=n;i++){
        if(!v[i])p[tot++]=i,miu[i]=-1;
        for(j=0;j<tot&&i*p[j]<=n;j++){
            v[i*p[j]]=1;
            if(i%p[j])miu[i*p[j]]=-miu[i];else break;
        }
    }for(i=1;i<n;i++)sum[i]=sum[i-1]+miu[i];
}
LL pow(LL a,LL b,LL p){if(b==0)return 1;LL t=1;for(a%=p;b;b>>=1LL,a=a*a%p)if(b&1LL)t=t*a%p;return t;}
int main(){
    read(T);
    mobius(100000);
    while(T--){
        read(n);
        ans=0; int mn=~0U>>1,mx=0;
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)read(a[i]),mn=min(a[i],mn),mx=max(a[i],mx),cnt[a[i]]++;
        for(int i=1;i<=200000;i++)cnt[i]+=cnt[i-1]; 
        for(int i=2;i<=mn;i++){
            if(!miu[i])continue;
            LL tmp=1; 
            for(int j=1;i*j<=100000;j++)tmp=tmp*pow(j,cnt[i*j+i-1]-cnt[i*j-1],mod)%mod;
            // j<=100000/i -> i*j<=100000 : TLE -> AC
            ans=(ans-tmp*miu[i]+mod)%mod;
        }printf("Case #%d: %lld\n",cas++,ans);
    }return 0;
}

HDU 6053 TrickGCD(分塊)