題意:
給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;
}