10.3
gcd
思路:
用 f(i) 表示 gcd 为 i的数对个数, g(i) 表示 gcd 为 i的倍数的数对个数。那么 f(i)=Σμ(d)g(d) ,我们只需要维护 g(1)~g(max(xi)))。记 s(i) 表示 i的倍数个数,那么 g(i)=s(i)*(s(i)-1)/2,我们只需要在加入删除一个数时枚举它的因数修改s即可。
时间复杂度 O(msqrt(max(xi))
%Doggu
dfs构造因数,
注释中有推导
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define LL long long
#define N 200010
#define lim 500010
using namespace std;
int cnt[lim];
bool in[N], vis[lim];
int stack[7], top;
LL ans;
int prime[lim/10], ptot, minp[lim], mu[lim];
vector<int> PR[N];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void init() {
mu[1] = 1;
for(int i=2; i<=lim-10; i++) {
if(!vis[i]) prime[++ptot] = i, minp[i] = i, mu[i] = -1;
for(int j=1; j<=ptot; j++) {
if((LL)i*prime[j] > lim) break;
vis[i * prime[j]] = 1;
minp[i * prime[j]] = prime[j];//预处理出最小的质因数
mu[i * prime[j]] = -mu[i];
if(i % prime[j] == 0) { mu[i * prime[j]] = 0; break;}
}
}
}
void modify(int x) {
vector<int>::iterator end = PR[x].end();
if( !in[x] )//选择状态
for(vector<int>::iterator it = PR[x].begin(); it != end; it++)
ans += mu[*it] * cnt[*it], cnt[*it]++;//此数显然可以整除这个质因数
else
for(vector<int>::iterator it = PR[x].begin(); it != end; it++)
cnt[*it]--, ans -= mu[*it] * cnt[*it];
in[x] ^= 1;
printf("%I64d\n", ans);
}
/*
在集合S中加入一个数a,求其中与它互质的数的个数
也就是求 singma([gcd(xi,a)==1])
= singma([gcd(xi,a)==1]) = singma(singma(mu(d))) (d|n)
= singma(mu(d)*(S中d的倍数的个数)) (d|n)
*/
inline void DFS(int i, int deep, int num) {
if(deep == top + 1) { PR[i].push_back(num); return ;}
DFS(i, deep+1, num*stack[deep]);
DFS(i, deep+1, num);
}//构造出来了一个序列:包含了所有的d
int main() {
freopen ("gcd.in", "r", stdin);
freopen ("gcd.out", "w", stdout);
init();
int n, q, x, tmp;
n = read(); q = read();
for(int i=1; i<=n; i++) {
x = read();
top = 0;
while(x != 1) {
stack[++top] = minp[x];
tmp = minp[x];
while(x % tmp == 0) x /= tmp;
}
DFS(i, 1, 1);
}
while( q-- ) {
x = read();
modify( x );
}
return 0;
}