题目链接:
题目大意:
给出一列数,最开始集合 为空,然后q次操作,每次给出一个x,如果第x个数存在,那么删去,不存在添加,问操作完互质的数有多少对
题目分析:
问互质的数的对数,裸裸的数论题
首先f(k)定义为gcd(ai,aj)(1<=i<j<=n) == k的对数
定义F(k)为k|gcd(ai,aj)(1<=i<j<=n)的对数
那么根据莫比乌斯反演定理的第二种形式得到f(k) = sigma(d>=1)[u(d)*F(d*k)]
定义cnt[i]为集合中是i的倍数的数有多少个
那么F(k) = C(2 , cnt[k] ) 结论很显然
所以我们可以利用莫比乌斯反演,在每次根据要添加或删除的数修正结果,然后输出即可
也就是对给出的数分解质因数,然后因为如果当前数含有某个质因数的2次方及以上,u等于0
而且因为2*3........*17 > 500000,所以质因数的个数不会超过6个,只要枚举2^6种影响到的F(k)即可
总的复杂度q*2^6,绝对的秒出
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#define MAX 500007
using namespace std;
typedef long long LL;
int n,q,x;
int a[MAX];
int u[MAX];
int used[MAX];
int cnt[MAX];
int maxPrime[MAX];
vector<int> v;
LL ans;
void init ( )
{
memset ( maxPrime , -1 , sizeof ( maxPrime ) );
for ( int i = 1 ; i < MAX ; i++ ) u[i] = 1;
for ( int i = 2 ; i < MAX ; i++ )
{
if ( ~maxPrime[i] ) continue;
for ( int j = 1 , t = i ; t < MAX ; j++,t += i )
{
maxPrime[t] = i;
u[t] = j%i==0?0:-u[t];
}
}
}
void update ( int x , int f )
{
v.clear();
while ( x > 1 )
{
int p = maxPrime[x];
v.push_back(p);
while ( x%p == 0 )
x /= p;
}
int m = v.size();
int total = 1<<m;
for ( int i = 0 ; i < total ; i++ )
{
int temp = 1;
for ( int j = 0 ; j < m ; j++ )
if ((1<<j)&i)
temp *= v[j];
//cout << "temp : " << temp << endl;
if ( !f )
{
//cout <<"YES :" << ans << endl;
ans += cnt[temp]*u[temp];
//cout << "ANS : " << ans << endl;
cnt[temp]++;
}
else
{
cnt[temp]--;
ans -= cnt[temp]*u[temp];
}
}
}
int main ( )
{
init();
while ( ~scanf ( "%d%d" , &n , &q ) )
{
for ( int i = 0 ; i < n ; i++ )
scanf ( "%d" , &a[i] );
memset ( used , 0 , sizeof ( used ) );
ans = 0;
while ( q-- )
{
scanf ( "%d" , &x );
update ( a[x-1] , used[x-1] );
used[x-1] ^= 1;
printf ( "%lld\n" , ans );
}
}
}