HDU 6053 TrickGCD(分塊)
阿新 • • 發佈:2017-07-30
%d space 復雜 cstring 前綴 == str 結果 logs
數值分塊計算的復雜度為n+n/2+n/3+n/4+n/5+……+n/n=nlogn。
對於計算結果,我們需要進行容斥,奇數次素數乘的系數為1,偶數次素數乘的系數為-1,
對於出現素數冪的合數其系數為0,
我們發現這個容斥恰好是莫比烏斯函數的相反數,因此我們取反即可。
這有個小小的優化,對於系數為0的情況,我們可以直接跳過,不進行計算。
【題目鏈接】 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(分塊)