Ccodeforces 547C Mike and Foam(容斥原理)

参考自大佬qianfujia 的博客代码

题目链接:Mike and Foam

题意:有n个数,q次操作,每次可以把一个数放入数组或取出,问每次操作后有多少对互质的数

题解:我们可以对于每个新加入的数计算当前数组中有多少个元素和其不互质,那么互质个数=当前数组中元素个数-不互质个数,我们知道任何一个数都可以表示成质因子的乘积,那么任意两个拥有相同质因子的数一定不互质,由于数据a<1e5,我们可以直接暴力枚举质因子,而去重部分我们用容斥原理处理,我们将只由质因子的单次幂乘积组成的数都存下,可以先打表确定其贡献的正负(例如:当a同时含有p1=2,p2=3时,我们应当要减去因子为6的数量来去重---容斥);

代码如下(参考自大佬代码(有点难懂),个人注释的很详细)

#include<iostream>
#include<stack>
#include<list>
#include<set>
#include<vector>
#include<algorithm>
#include<math.h>
#include<numeric>
#include<map>
#include<cstring>
#include<queue>
#include<iomanip>
#include<cmath>
#include<queue>
#include <bitset>
#include<unordered_map>
	#ifndef local
	#define endl '\n'
#endif */
#define mkp make_pair
using namespace std;
using std::bitset;
typedef long long ll;
typedef long double ld;
const int inf=0x3f3f3f3f;
const ll MAXN=2e6+10;
const ll N=5e5+100;
const ll mod=1e9+7;
const ll hash_p1=1610612741;
const ll hash_p2=805306457;
const ll hash_p3=402653189;
//-----------------------------------------------------------------------------------------------------------------*/
// ll head[MAXN],net[MAXN],to[MAXN],edge[MAXN]/*流量*/,cost[MAXN]//费用;
/* 
void add(ll u,ll v,ll w,ll s){
	to[++cnt]=v;net[cnt]=head[u];edge[cnt]=w;cost[cnt]=s;head[u]=cnt;
	to[++cnt]=u;net[cnt]=head[v];edge[cnt]=0;cost[cnt]=-s;head[v]=cnt;
}
struct elemt{
	int p,v;
};
struct comp{
	public:
		bool operator()(elemt v1,elemt v2){
			return v1.v<v2.v;
		}
};
-----------------------------------
求[1,MAXN]组合式和逆元 
ll mi(ll a,ll b){
	ll res=1;
	while(b){
		if(b%2){
			res=res*a%mod;
		}	
		a=a*a%mod;
	}
	return res;
}
ll fac[MAXN],inv[MAXN]
fac[0]=1;inv[0]=1;
for(int i=1;i<=MAXN;i){
	fac[i]=(fac[i-1]*i)%mod;
	inv[i]=mi(fac[i],mod-2);
}
ll C(int m,int n){//组合式C(m,n); 
	if(!n){
		return 1;
	}
	return fac[m]*(inv[n]*inv[m*-n]%mod)%mod;
}
---------------------------------
 unordered_map<int,int>mp;
//优先队列默认小顶堆 , greater<int> --小顶堆  less<int> --大顶堆  
priority_queue<elemt,vector<elemt>,comp>q;
	set<int>::iterator it=st.begin();
*/
// vector<vector<int>>edge; 二维虚拟储存坐标 
//-----------------------------------------------------------------------------------------------------------------*/
  //map<int,bool>mp[N];
int s[N]; 
void init(){//容斥原理算各个数贡献系数,例如|A|(一个质因子),为正贡献|AA|(两个质因子)为负贡献|AAA|为正贡献... 
	for(int i=2;i<=500000;i++){
		int x=i;
		s[i]=-1;//储存贡献系数, 
		for(int j=2;j*j<=x;j++){
			if(x%j==0){
				x/=j;s[i]=-s[i];
				if(x%j==0){//不是一次幂的纯质因子组成 (p1为A,p2为B,则p1p2为|A+B|) 
					s[i]=0;
					x=1;
					break;
				}
			}
		}
		if(x!=1){
			s[i]=-s[i];
		}
	}
}
int a[N];
int vis[N];
int num[N];//该因子在当前队列出现次数 
vector<int>b[N];
int main(){
/*cout<<setiosflags(ios::fixed)<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(不含整数部分)*/
/*cout<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(含整数部分)*/
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//同步流
init();
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
	cin>>a[i];
	for(int j=1;j*j<=a[i];j++){
		if(a[i]%j==0){
			if(s[j]!=0){
				b[i].push_back(j);//存放|A|,|AA|,|AAA|... 
			}
			if(a[i]!=j*j&&s[a[i]/j]!=0){
				b[i].push_back(a[i]/j);//同上 
			} 
		}
	}
}
ll ans=0,tot=0;//ans存对应答案,tot记录当前队内元素个数
while(q--){
	int x;
	cin>>x;
	if(a[x]==1){//特判1,和所有元素互质 
		if(vis[x]){
			ans-=tot-1;
			tot--;
		}
		else{
			ans+=tot++;
		}
	}
	else if(vis[x]){
		ll res=0;//该点当前加入进时会和多少个点不互质
		for(int i=0;i<b[x].size();i++){
			res+=s[b[x][i]]*num[b[x][i]];
			num[b[x][i]]--;//该因子在当前队列出现次数减少一次(当前x要被删除) 
		}
		ans-=tot-res;//和当前元素互质个数为总数量-不互质个数
		tot--; 
	}
	else if(!vis[x]){
		ll res=0;
		for(int i=0;i<b[x].size();i++){
			res+=s[b[x][i]]*num[b[x][i]];
			num[b[x][i]]++; 
		}
		ans+=tot-res; 
		tot++; 
	}
	vis[x]^=1;
	cout<<ans<<endl;
} 
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值