cf645F 数论

题目链接

cf645F

题意

给定长度为n的序列,每次询问向序列中加入一个数,询问序列中所以k元组的gcd之和。

题解

记S表示所以k元组的集合,我们要求的就是 

∑s∈S gcd(s)= ∑s∈S ∑d|gcd(s) ϕ(d)

=∑d ϕ(d)∗ ∑s∈S [(g)cd(s)=d] =∑d ϕ(d)∗C(k,s[d])

由欧拉函数的性质  m=sigma(phi(d)),d|mm=sigma(phi(d)),d|m  

其中ϕ(d)ϕ(d)表示d的欧拉函数值,s[d]s[d]表示含有因子d的数的个数。我们可以预处理出这两个值。 
先处理出初始答案,对于每一次询问我们只需要O()O(因子个数)的时间就能动态更新答案。


#include<cstdio>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int mo=1000000007,N=1000010;
int n,m,k,ans,a[N],s[N];
int p[N],pn,f[N],phi[N],inv[N],fac[N];

int power(long long x,int y){
    int tmp=1;
    for(;y;y>>=1,x=x*x%mo)
    if(y&1) tmp=tmp*x%mo;
    return tmp;
}
void init(){
    phi[1]=1;
    for(int i=2;i<=1000000;i++){
        if(!f[i]) p[++pn]=i,phi[i]=i-1;
        for(int j=1;j<=pn&&p[j]*i<=1000000;j++){
            f[p[j]*i]=1;
            if(i%p[j]==0){
                phi[i*p[j]]=p[j]*phi[i];
                break;
            }
            phi[i*p[j]]=(p[j]-1)*phi[i];
        }
    }
    fac[0]=1;
    for(int i=1;i<=1000000;i++) fac[i]=1ll*fac[i-1]*i%mo;
    inv[1000000]=power(fac[1000000],mo-2);
    for(int i=999999;~i;i--) inv[i]=1ll*inv[i+1]*(i+1)%mo;
}
long long C(int n,int m){
    if(n<m) return 0;
    return 1ll*fac[n]*inv[n-m]%mo*inv[m]%mo;
}
void calc(int x){
    ans=(ans-phi[x]*C(s[x],k))%mo;
    s[x]++;
    ans=(ans+phi[x]*C(s[x],k))%mo;
    if(ans<0) ans+=mo;
}
int main(){
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    for(int i=1;i<=n;i++)
     for(int j=1;j*j<=a[i];j++)
     if(a[i]%j==0){
        s[j]++;
        if(j*j!=a[i]) s[a[i]/j]++;
     }
    init();
    for(int i=1;i<=1000000;i++)
    ans=(ans+phi[i]*C(s[i],k))%mo;
    for(int i=1;i<=m;i++){
        scanf("%d",&a[1]);
        for(int j=1;j*j<=a[1];j++)
        if(a[1]%j==0){
            calc(j);
            if(j*j!=a[1]) calc(a[1]/j);
        }
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值