Mike and Foam

题目描述

题解

对于每次询问可以对新加入的数或移除的数进行单独贡献计算
假设新加入或移除的数为\(x=p_1^{\alpha_1}p_2^{\alpha_2}···p_k^{\alpha_k}\)
显然每个质数的系数无关紧要,所以对一新加入\移除的数只考虑已存在的数中是否有\(p_1,p_2,···,p_k\)
因为\(x\leq 5*10^5 <2*3*5*7*11*13*17=510510\),所以x最多有6个不同的质因子

那么如何找已存在的数中没有这些质因子的个数
可以考虑容斥原理
\(设a_i为已存在的数中能被i整除的个数\)
则新加\移除一个数x的贡献为
\(N(\prod_{i=1}^{x的质因子个数k} \:\:\:\:\:\:(1-p_i))=\sum_{i=0}^{k}(-1)^i \cdot \sum_{从x的质因子中选i个相乘为t} N(a_t)\)
每次后询问更新\(N(a_i)\)的值

总时间复杂度为\(O(2^6q)\)

#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<queue>
#include<stdlib.h>
#define ll long long 
using namespace std;
const int maxn=500000+10101;
const int MOD=12345678;
const int inf=2147483647;
int read(){
	int x=0,f=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
	for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
	return x*f;
}
int n,q,a[maxn],br[maxn][8],book[maxn],value[maxn],ma[8];
int tot,prime[maxn],is[maxn];
void get_prime(){
	for(int i=2;i<=200000;i++){
		if(!is[i])prime[++tot]=i;
		for(int j=1;j<=tot && prime[j]<=200000/i;j++){
			is[i*prime[j]]=1;
			if(!i%prime[j])break;
		}
	}
	return ;
}
ll ans,sum;
int tt,ji[101];
void dfs(int pos,int step,int k){	//容斥
	if(step>br[pos][0]){
		ll s1=1;
		for(int i=1;i<=br[pos][0];i++){if(ma[i])s1*=br[pos][i];}
		sum=sum+k*value[s1];ji[++tt]=s1;
		return ;
	}
	dfs(pos,step+1,k);if(br[pos][step]==1)return; //对于a_pos=1的特判
	ma[step]^=1;dfs(pos,step+1,k*(-1));ma[step]^=1;
	return ;
}
ll solve(int x){
	int xi=1;
	if(book[x])xi=-1,book[x]=0;
	else book[x]=1;
	memset(ma,0,sizeof(ma));sum=0;tt=0;
	dfs(x,1,1);ans=ans+xi*sum;
	for(int i=1;i<=tt;i++)value[ji[i]]+=xi;
	if(a[x]==1 && xi==-1)ans++; //可能1会被多减一次,因为gcd(1,1)=1
	return ans;
}
int main(){
	n=read();q=read();get_prime();
	for(int i=1;i<=n;i++){
		a[i]=read();int now=a[i];
		if(a[i]==1){br[i][++br[i][0]]=1;continue;}
		for(int j=1;j<=tot && now>1 && prime[j]<=sqrt(now);j++){
			if(now%prime[j]==0){
				br[i][++br[i][0]]=prime[j];
				while(now%prime[j]==0)now/=prime[j];
			}
		}
		if(now>1)br[i][++br[i][0]]=now;
	}
	for(int i=1;i<=q;i++){int x=read();printf("%lld\n",solve(x));}
	return 0;
}

posted @ 2021-10-25 21:18  I_N_V  阅读(30)  评论(0编辑  收藏  举报