题意:
给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;
}