互质对(51nod1439)

来源:互联网 发布:刀具查询软件 编辑:程序博客网 时间:2024/06/07 07:31

题目大意

有n个数字,a[1],a[2],…,a[n]。有一个集合,刚开始集合为空。然后有一种操作每次向集合中加入一个数字或者删除一个数字。每次操作给出一个下标x(1 ≤ x ≤ n),如果a[x]已经在集合中,那么就删除a[x],否则就加入a[x]。
问每次操作之后集合中互质的数字有多少对。
注意,集合中可以有重复的数字,两个数字不同当且仅当他们的下标不同。

Input

单组测试数据。
第一行包含两个整数n 和 q (1 ≤ n, q ≤ 2 × 10^5)。表示数字的种类和查询数目。
第二行有n个以空格分开的整数a[1],a[2],…,a[n] (1 ≤ a[i] ≤ 5 × 10^5),分别表示n个数字。
接下来q行,每行一个整数x(1 ≤ x ≤ n),表示每次操作的下标。

Output

对于每一个查询,输出当前集合中互质的数字有多少对。

样例输入

2 3
1 1
1
2
1

样例输出

0
1
3
5
6
2

Solution

我们可以发现每次只(不)选一个数,所以只要统计这次操作对答案的贡献就可以。
本题据说可以用莫比乌斯反演做,可本蒟蒻不会,只好各种乱搞。
对于每次操作,只要找和它互质的数有几个,也可以统计和它不互质的数有几个。枚举它的质因数,再减去这些质因数的倍数就可以。但又会重复减,所以需要根据容斥原理,跑一遍dfs,如加进一个12时,统计和它的公约数为2的数的个数和公约数为3的数的个数,发现公约数为6的计算了两次,再减掉就可以。后来一位大佬告诉我这和反演是等价的。

#include<cstdio>#include<algorithm>#include<cstring>using namespace std;typedef long long ll;ll ans;int x;int prime[500010][21],cnt[500010],a[200010],flag[500010];void dfs(int t,int num,int tag,int z){    if (t>prime[x][0])    {        if (z==-1)            cnt[num]--;        ans+=cnt[num]*tag;        if (z==1) cnt[num]++;        return;    }    dfs(t+1,num,tag,z);    dfs(t+1,num*prime[x][t],-tag,z);}int main(){    int n,q;    scanf("%d%d",&n,&q);    for (int i=1;i<=n;i++) scanf("%d",&a[i]);    memset(flag,0,sizeof(flag));    for (int i=2;i<=500000;i++)//直接暴力筛质数    {        if (flag[i]) continue;        prime[i][0]=1;        prime[i][1]=i;        for (int j=i+i;j<=500000;j+=i)        {            flag[j]=1;            prime[j][++prime[j][0]]=i;        }    }    memset(flag,0,sizeof(flag));    ans=0;    while (q--)    {        scanf("%d",&x);        flag[x]^=1;        if (flag[x])        {            x=a[x];            dfs(1,1,1,1);        }        else        {            x=a[x];             dfs(1,1,-1,-1);        }        printf("%lld\n",ans);    }    return 0;}
原创粉丝点击