题目链接
题意
给定长度为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;
}