NOIP模拟 gcd 数学

题面不给……题意:动态增减集合中元素个数,动态求$gcd(i,j)==1$的数的个数。

坦白地讲题解并没完全看懂……于是听$lc$开了一会车(快开完时$xyz$强行砸场导致烂尾),结合着自己理解又推一遍想明白了……

在本题之中,我们设$f(x)$为$gcd(i,j)==x$的数的个数,$g(x)$为$gcd(i,j)==kx$的数的个数。很显然$f(x)=g(x)-f(2x)-f(3x)...$,这样推下去,我们就会发现这个系数其实是符合莫比乌斯函数的,最后大力化简就是:

\[ \sum_{d}^{} {μ(d)*g(d)} =f(d)\]。

现在定义$s(i)$为当前选中的集合中为$i$倍数数的个数,可以看出,$g(i)=s(i)*(s(i)-1)/2$。

然后我们就可以预处理莫比乌斯函数,在每一次修改元素个数时候动态维护$s(i)$。

但是我们暴力求$ans$显然会$T$……于是考虑每一次加入或删除元素对于最后结果贡献。

当我们插入一个元素,对于每一个可以整除它的$i$,$Δans=μ(i)*((s(i)+1)*s(i)-s(i)*(s(i)-1))/2=μ(i)s(i)$。同理,删除时,对于每一个这样的$i$,$Δans=μ(i)*((s(i)-2)*(s(i)-1)-s(i)*(s(i)-1))/2=-μ(i)(s(i)-1)$。然后我们根据这个规律转移即可。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 const int maxn=200005,maxx=500005;
 7 bool notprime[maxx];int prime[maxx],cnt,mu[maxx];
 8 void init()
 9 {
10     mu[1]=1;
11     for(int i=2;i<=500000;i++)
12     {
13         if(!notprime[i])prime[++cnt]=i,mu[i]=-1;
14         for(int j=1;j<=cnt&&i*prime[j]<=500000;j++)
15         {
16             notprime[i*prime[j]]=1;
17             if(i%prime[j])mu[i*prime[j]]=-mu[i];
18             else {mu[i*prime[j]]=0;break;}
19         }
20     }
21 }
22 int n,m,a[maxn],s[maxx],maxv,t[maxn];
23 int haha()
24 {
25     scanf("%d%d",&n,&m);init();
26     for(int i=1;i<=n;i++)scanf("%d",&a[i]),maxv=max(maxv,a[i]);
27     long long ans=0;
28     for(int i=1;i<=m;i++)
29     {
30         int x;scanf("%d",&x);int sgn=t[x]?-1:1;
31         for(int j=1;j*j<=a[x];j++)
32             if(!(a[x]%j))
33             {
34                 if(sgn==1)ans+=1ll*mu[j]*s[j];else ans-=1ll*(s[j]-1)*mu[j];s[j]+=sgn*1;
35                 if(j*j<a[x])
36                 {
37                     if(sgn==1)ans+=1ll*mu[a[x]/j]*s[a[x]/j];else ans-=1ll*(s[a[x]/j]-1)*mu[a[x]/j];
38                     s[a[x]/j]+=sgn*1;
39                 }
40             }
41         t[x]^=1;
42         printf("%lld\n",ans);
43     }
44 }
45 int sb=haha();
46 int main(){;}
C

 

posted @ 2017-10-03 21:50  ccc000111  阅读(259)  评论(0编辑  收藏  举报