一、题目
题目翻译
有一个长度为 n n n的数组,每次向其插入一个数,问从中选 k k k个数求 gcd \gcd gcd的和,输出插入数字后的值。
二、解法
看到 gcd \gcd gcd就可以往莫比乌斯反演的方向想了。
设 f ( i ) f(i) f(i)为 gcd \gcd gcd为 i i i的方案数,答案为: ∑ i × f ( i ) \sum i\times f(i) ∑i×f(i)
设 F ( i ) F(i) F(i)为 gcd \gcd gcd是 i i i的倍数的方案数,设 c n t [ i ] cnt[i] cnt[i]数组中为 i i i倍数的个数
F
(
i
)
=
∑
i
∣
d
f
(
d
)
=
C
(
c
n
t
[
i
]
,
k
)
F(i)=\sum_{i|d}f(d)=C(cnt[i],k)
F(i)=i∣d∑f(d)=C(cnt[i],k)
f
(
i
)
=
∑
i
∣
d
F
(
d
)
×
μ
(
d
i
)
f(i)=\sum_{i|d}F(d)\times\mu(\frac{d}{i})
f(i)=i∣d∑F(d)×μ(id)考虑加入一个数的答案的变化,首先直接影响到了
c
n
t
cnt
cnt数组,设
x
x
x为这个数的因数,那么
c
n
t
[
x
]
cnt[x]
cnt[x]加
1
1
1,结合上面的式子,不难发现答案会增加:
(
C
(
c
n
t
[
x
]
,
k
)
−
C
(
c
n
t
[
x
]
−
1
,
k
)
)
∑
i
∣
x
i
×
μ
(
i
)
(C(cnt[x],k)-C(cnt[x]-1,k))\sum_{i|x}i\times\mu(i)
(C(cnt[x],k)−C(cnt[x]−1,k))i∣x∑i×μ(i)我们把一开始的数组当前空,然后插入
n
+
q
n+q
n+q个数,明显后面的式子可以
O
(
n
log
n
)
O(n\log n)
O(nlogn)预处理,那么总的复杂度就是
O
(
n
n
)
O(n\sqrt n)
O(nn),贴个代码
q
w
q
qwq
qwq
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
const int M = 1000005;
const int jzm = 1e9+7;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,k,q,ans,cnt,p[M],f[M],vis[M],mu[M],g[M],inv[M],fac[M];
void init(int n)
{
inv[0]=inv[1]=fac[0]=fac[1]=1;
for(int i=2;i<=n;i++) inv[i]=inv[jzm%i]*(jzm-jzm/i)%jzm;
for(int i=2;i<=n;i++) fac[i]=fac[i-1]*i%jzm;
for(int i=2;i<=n;i++) inv[i]=inv[i]*inv[i-1]%jzm;
mu[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i])
{
p[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt && i*p[j]<=n;j++)
{
vis[i*p[j]]=1;
if(i%p[j]==0) break;
mu[i*p[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
g[j]=(g[j]+i*mu[j/i])%jzm;
}
int C(int n,int m)
{
if(n<m) return 0;
return fac[n]*inv[m]%jzm*inv[n-m]%jzm;
}
void upd(int x)
{
f[x]++;
ans=(ans+(C(f[x],k)-C(f[x]-1,k))%jzm*g[x]%jzm)%jzm;
}
void dec(int x)
{
for(int i=1;i*i<=x;i++)
if(x%i==0)
{
upd(i);
if(i*i!=x) upd(x/i);
}
}
signed main()
{
n=read();k=read();q=read();
init(1e6);
for(int i=1;i<=n;i++)
dec(read());
for(int i=1;i<=q;i++)
{
dec(read());
printf("%lld\n",(ans+jzm)%jzm);
}
}