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(){;}
只要是活着的东西,就算是神我也杀给你看。