HDU 6053 TrickGCD(莫比烏斯函式)
阿新 • • 發佈:2018-12-30
題意:給出陣列 AA ,問有多個種 BB 陣列滿足所給條件。
思路
如果暴力肯定會超時,所以用到劃區的方法做,比如3,4,5除以3結果都是1,小數點後的都去除,所以除數是3的時候可以把3,4,5劃分在一起,一起用快速冪計算。
思路
設 gcd(b1,...bn) = k (k >= 2),此時 k 對答案的貢獻為 (a1/k)*(a2/k)*(a3/k)*...*(an/k)
根據容斥原理,ans = +[k=一個素數之積 時對答案的貢獻]
-[k=兩個素數之積 時對答案的貢獻]
+[k=三個素數之積 時對答案的貢獻]
...
這麼去重要考慮很多,所以使用莫比烏斯函式去重,雖然我不知道莫比烏斯函式怎麼來的,我知道這是一個定理,去重的時候異常方便。
莫比烏斯函式完整定義的通俗表達:
1)莫比烏斯函式μ(n)的定義域是N
2)μ(1)=1
3)當n存在平方因子時,μ(n)=0
4)當n是素數或奇數個不同素數之積時,μ(n)=-1
5)當n是偶數個不同素數之積時,μ(n)=1
對於每個k,對sum[]進行埃式篩法的分塊,即根據k的倍數分塊 此時每個k的貢獻 = 1^(sum[2k-1]-sum[k-1]) * 2^(sum[3k-1]-sum[2k-1]) * 3^(sum[4k-1]-sum[3k-1]) ... 就做到 O(n(logn)^2)
如果暴力肯定會超時,所以用到劃區的方法做,比如3,4,5除以3結果都是1,小數點後的都去除,所以除數是3的時候可以把3,4,5劃分在一起,一起用快速冪計算。
#include "stdio.h" #include "string.h" #include <iostream> #include <queue> #include <math.h> #include <algorithm> using namespace std; const int maxn=200000+10; const int mod=1000000000+7; int mu[maxn]; void init() { mu[1]=1; for(int i=1; i<maxn; i++) for(int j=i+i; j<maxn; j+=i) mu[j]-=mu[i]; } long long mul(long long a,int b) { long long ans=1; while(b>0) { if(b&1){ans*=a;ans%=mod;} a=a*a; a%=mod; b/=2; } return ans; } int main() { memset(mu,0,sizeof(mu)); init(); int t,cnt=0; int sum[maxn]; scanf("%d",&t); while(t--) { int n,x,min1,max1; memset(sum,0,sizeof(sum)); scanf("%d",&n); min1=200000; max1=-1; for(int i=1;i<=n;i++) { scanf("%d",&x); sum[x]++; min1=min(x,min1); max1=max(max1,x); } for(int i=1;i<=2*max1;i++) sum[i]+=sum[i-1]; long long res=0; for(int i=2;i<=min1;i++) { long long temp=1; if(mu[i]) { for(int j=i;j<=max1;j+=i) { temp*=mul(j/i,sum[j-1+i]-sum[j-1]); temp%=mod; } res=(res-temp*mu[i]+mod)%mod;//莫比烏斯函式偶加奇減,所以減去這個函式。 } } printf("Case #%d: %lld\n",++cnt,res); } return 0; }