您的位置:首页 > 其它

Code Forces 547 C. Mike and Foam(素因子分解+容斥)

2015-09-04 08:32 441 查看
Description

有n张牌,每张牌上都写着一个数字ai,开始时所有牌都在手上。现在有q个询问,每次询问指定一个数x,如果第x张牌在你手上,就要把它放在桌子上;如果在桌子上,就要拿回手中。每次这样拿完或者放完牌,要求回答当前桌子上的牌中,有多少对(i,j)(i

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
#define maxn 555555
ll n,q,a[maxn];
ll res,prime[maxn];
ll tol,num[2*maxn];//tol记录桌上牌数,num[i]记录桌上以i为因子的牌数
ll ans;//ans记录答案值
bool vis[maxn];
void get_prime(ll n)///对n素分解
{
res=0;
for(ll i=2;i*i<=n;i++)
{
if(n%i==0)
{
prime[res++]=i;
while(n%i==0)
n/=i;
}
}
if(n!=1)
prime[res++]=n;
}
void sub(ll n)//将第n张牌从桌子上拿走
{
vis
=false;//取消标记
ll sum=0;//统计桌上原有牌中与n互素的牌数
for(ll i=1;i<(1<<res);i++)//容斥
{
ll temp=1,ret=0;
for(ll j=0;j<res;j++)
if(i&(1<<j))
temp*=prime[j],ret++;
num[temp]--;//以temp为因子的牌数减一
if(ret%2) sum+=num[temp];
else sum-=num[temp];
}
tol--;//桌上牌数减一
ans-=(tol-sum);
printf("%I64d\n",ans);
return ;
}
void add(ll n)//将第n张牌放在桌子上
{
vis
=true;//标记这张牌
ll sum=0;//统计桌上原有牌中与n互素的牌数
for(ll i=1;i<(1<<res);i++)//容斥
{
int temp=1,ret=0;
for(ll j=0;j<res;j++)
if(i&(1<<j))
temp*=prime[j],ret++;
if(ret%2) sum+=num[temp];
else sum-=num[temp];
num[temp]++;//以temp为因子的牌数加一
}
ans+=(tol-sum);
tol++;//桌上牌数加一
printf("%I64d\n",ans);
return ;
}
int main()
{
scanf("%I64d%I64d",&n,&q);
for(ll i=1;i<=n;i++)
scanf("%I64d",&a[i]);
memset(vis,false,sizeof(false));//初始化
memset(num,0,sizeof(num));//初始化
tol=0;
ans=0;
while(q--)
{
ll x;
scanf("%I64d",&x);
get_prime(a[x]);//对第x张牌的数字素分解
if(vis[x])//x已经在桌上则将其拿走
sub(x);
else//x不在桌上则将其放在桌上
add(x);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: