题意:每次选择一个酒杯放在吧台,或从吧台拿下去,每次操作后询问吧台上的数字有多少对是互素的。
思路:首先把所有数拆出它的质因子,已经所有质因子的乘积。然后用容斥原理,求出吧台上有多少其它的和它互素的数字。
AC代码如下:
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int T,t,n,m;
int prime[100010],tot;
bool vis[500010];
int p[500010][10],p2[500010][110];
bool odd[500010];
int val[200010],num[500010];
void init()
{
int i,j,k,S,S2,a,b;
for(i=2;i<=500000;i++)
if(!vis[i])
{
prime[++tot]=i;
for(j=i*2;j<=500000;j+=i)
vis[j]=1;
}
//printf("tot=%d\n",tot);
for(i=1;i<=tot;i++)
{
k=prime[i];
for(;k<=500000;k+=prime[i])
{
p[k][0]++;
p[k][p[k][0]]=prime[i];
}
}
for(i=1;i<=500000;i++)
{
k=p[i][0];
S2=(1<<k)-1;
for(S=1;S<=S2;S++)
{
a=1;b=0;
for(j=1;j<=k;j++)
if(S&(1<<(j-1)))
{
b++;
a*=p[i][j];
}
p2[i][0]++;
p2[i][p2[i][0]]=a;
if(b&1)
odd[a]=1;
else
odd[a]=0;
}
}
}
int main()
{
int i,j,k,q;
ll ret,temp=0,ans=0;
init();
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)
scanf("%d",&val[i]);
memset(vis,0,sizeof(vis));
while(q--)
{
scanf("%d",&k);
if(!vis[k])
{
vis[k]=1;
k=val[k];
ret=temp;
for(i=1;i<=p2[k][0];i++)
{
j=p2[k][i];
if(odd[j])
ret-=num[j];
else
ret+=num[j];
}
ans+=ret;
temp++;
for(i=1;i<=p2[k][0];i++)
num[p2[k][i]]++;
}
else
{
vis[k]=0;
k=val[k];
temp--;
for(i=1;i<=p2[k][0];i++)
num[p2[k][i]]--;
ret=temp;
for(i=1;i<=p2[k][0];i++)
{
j=p2[k][i];
if(odd[j])
ret-=num[j];
else
ret+=num[j];
}
ans-=ret;
}
printf("%I64d\n",ans);
}
}