【2020-9-12比赛】 题解【互质数对】
这次比赛有事所以没打 0分((
A.互质数对
大概意思是给了一个数列,然后每次往数列里丢一个下标,如果下标在数列里就取出下标所对应的数,没有就加入
然后每次ans的变化量是数列里下标所对应的数与正在操作的下标所对应的数的互质的个数
我们考虑我们正在进行操作的下标X,答案变化量为
\(\sum\limits_{y\in S}if(gcd(a[x],a[y]) = 1)\)
我们考虑这个求和式子 \(\sum\limits_{y\in S}if(gcd(a[x],a[y]) = 1) = card(S) - \sum\limits_{y\in S}if(gcd(a[x],a[y]) != 1)\)
我们考虑X的两个质因数分子a,b f(x) 表示 在S的下标中对应的数x的倍数的数量 我们知道S中被a,b整除的数量为 f(a) + f(b) - f(a*b) 就是一个容斥
我们把这个推广到任意多个就行了
在这里介绍莫比乌斯函数
我们通过容斥或者莫比乌斯反演可以得到 \(\sum\limits_{y\in S}if(gcd(a[x],a[y]) = 1) = \sum\limits_{y\in S}\sum\limits_{d|a[x]且d|a[y]}\mu(d) = \sum\limits_{d|a[x]}\mu(d)\)
莫比乌斯函数的求法可以在杜式筛时顺带求出 首先令 \(\mu(1~n)=1\) 对于每个质数p \(\mu(p)=-1\) 对p的每个倍数x检查 若x被\(p^2\)整除 \(\mu(x)=0\) 否则 \(\mu(x)=-\mu(x)\)
代码
#include<iostream>
#include<cstdio>
using namespace std;
long long n,q;
long long mu[500005],v[500005];
long long a[500005],x,cnt[500005],ans;
bool use[500005];
void make(long long m)
{
for(long long i = 1;i <= m;i++)
mu[i] = 1;
for(long long i = 2;i <= m;i++)
{
if(!v[i])
{
mu[i] = -1;
for(long long j = 2 * i;j <= m;j += i)
{
v[j] = 1;
if((j / i) % i == 0) mu[j] = 0;
else
mu[j] *= -1;
}
}
}
}
int main()
{
freopen("pair.in","r",stdin);
freopen("pair.out","w",stdout);
scanf("%lld%lld",&n,&q);
make(500000);
for(long long i = 1;i <= n;i++)
scanf("%lld",&a[i]);
while(q--)
{
scanf("%lld",&x);
use[x] ^= 1;
if(use[x])
{
for(long long i = 1;i * i <= a[x];i++)
{
if(a[x] % i == 0)
{
ans += mu[i] * cnt[i];
cnt[i] += 1;
if(a[x] / i != i)
{
ans += mu[a[x] / i] * cnt[a[x] / i];
cnt[a[x] / i] += 1;
}
}
}
}
else
{
for(long long i = 1;i * i <= a[x];i++)
{
if(a[x] % i == 0)
{
cnt[i] -= 1;
ans -= mu[i] * cnt[i];
if(a[x] / i != i)
{
cnt[a[x] / i] -= 1;
ans -= mu[a[x] / i] * cnt[a[x] / i];
}
}
}
}
cout<<ans<<endl;
}
}(markdown还在熟悉 代码有点丑)