解题报告: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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值