解題報告:Codeforces Round #325(Div. 1) E. Present for Vitalik the Philatelist (莫比烏斯反演)

題目鏈接


題意:

給n(n<=5e5)個數ai(ai<=1e7),Vitalik會很開心,如果:

從中選出一個數x,在從剩下的數中選出一個非空集合S,滿足

S的gcd不爲1,gcd(S,x)爲1

詢問滿足的方案數


閒扯:

本來O(nlog(n))的想法,交上去一直T。。。

以爲卡常數,優化了還是T。。。

發現中間有個部分寫成O(n*sqrt(Ai))的級別。。。

改了後3000+ms過了。。。



思路:

爲了方便表述,作以下定義:

f ( x ):給定的n個數中與x互質的個數

g( x ):gcd(S)==x的集合數目

那麼答案爲:

接下了我們分別求g()和f():


num[d]:給定n個數中爲d的倍數的個數,可以在內預處理得到

那麼f()就能在得出

同樣g()可以在得出:


常數優化就不寫了,到這裏已經能求解問題的答案


代碼:

#include<bits/stdc++.h>

const int N = 1e7+10;
const int mod = 1e9+7;

using namespace std;

vector<int>pr;
bool Np[N];
int mu[N],_2[N],g[N],f[N],A[N],num[N];

inline void init(int n){
   _2[0] = mu[1] = 1;
   _2[1] = 2;
   for(int i=2;i<=n;i++){
      if( ( _2[i] = ( _2[i-1] << 1 ) ) >= mod ) _2[i] -= mod;
      if(!Np[i]){
         mu[i] = -1;
         pr.emplace_back(i);
      }for(int j=0,k=pr[0]*i;k<=n;k=pr[++j]*i){
         Np[k] = 1;
         if(i%pr[j]==0){
            mu[k] = 0;
            break;
         }mu[k] = -mu[i];
      }
   }for(int i=1;i<=n;i++){
      for(int j=i;j<=n;j+=i){
         num[i]+=A[j];
      }
   }
}


inline void oper(int x,int val){
   int m =sqrt(x+0.5);
   for(int i=1;i<=m;i++){
      if(x%i==0){
         num[i]+=val;
         num[x/i]+=val;
      }
   }if(m*m==x)num[m]-=val;
}

int main()
{
   int n,mx=0;
   scanf("%d",&n);
   for(int i=0,x;i<n;i++){
      scanf("%d",&x);
      A[x]++;mx = max(mx,x);
   }init(mx);
   long long ans = 0;
   for(int i=mx;i>=1;i--)if(num[i]){
      int cnt = mu[i] * num[i];
      if(cnt<0)cnt+=mod;
      if( (g[i] = _2[num[i]]-1 ) < 0)g[i]+=mod;
      if((f[i]+=cnt)>=mod)f[i]-=mod;
      for(int j=i<<1;j<=mx;j+=i){
         if(cnt&&(f[j]+=cnt)>=mod)f[j]-=mod;
         if( (g[i] -= g[j]) < 0  )g[i]+=mod;
      }
   }for(int i=2;i<=mx;i++)if(g[i]&&f[i])ans =   ( ans + 1LL * f[i] * g[i] ) % mod;
   printf("%I64d\n",ans);
   return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章