51Nod 1439 - 互质对(容斥+莫比乌斯函数)
题目链接 https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1439
【题目描述】
有n个数字,a[1],a[2],…,a[n]。有一个集合,刚开始集合为空。然后有一种操作每次向集合中加入一个数字或者删除一个数字。每次操作给出一个下标x(1 ≤ x ≤ n),如果a[x]已经在集合中,那么就删除a[x],否则就加入a[x]。
问每次操作之后集合中互质的数字有多少对。
注意,集合中可以有重复的数字,两个数字不同当且仅当他们的下标不同。
比如a[1]=a[2]=1。那么经过两次操作1,2之后,集合之后存在两个1,里面有一对互质。
Input
单组测试数据。
第一行包含两个整数n 和 q (1 ≤ n, q ≤ 2 × 10^5)。表示数字的种类和查询数目。
第二行有n个以空格分开的整数a[1],a[2],…,a[n] (1 ≤ a[i] ≤ 5 × 10^5),分别表示n个数字。
接下来q行,每行一个整数x(1 ≤ x ≤ n),表示每次操作的下标。
Output
对于每一个查询,输出当前集合中互质的数字有多少对。
Input示例
样例输入1
5 6
1 2 3 4 6
1
2
3
4
5
1
样例输入2
2 3
1 1
1
2
1
Output示例
样例输出1
0
1
3
5
6
2
样例输出2
0
1
0
【思路】
如果已经知道现在集合中有多少对互质对,现在新加入一个元素x,那么只要计算这个和集合中的所有元素能够形成多少个互质对,加到答案中去,删除元素同理. 问题就是要计算一个数字和一堆数字会组成多少互质对,反过来考虑问题,也可以计算组成非互质对的个数然后用集合的大小减去就得到互质对的个数
那非互质对怎么计算,假设新加入一个整数,原来集合中有一个元素,如果不是互质对,那么 同时 的值一定是的一个因子,这里我们就可以遍历的每一个大于1的因子,然后用容斥原理来计算了,具体怎么算这里举个例子,比如现在集合中已经有了5个元素,新加入一个,我们遍历的所有大于1的因子,如果集合中有约数为2的数,那么就会和中每一个有约数2的数产生一个非互质对,同理其它的约数都是,但是会算重,如果已经计算过中有约数为2的个数和约数为3的个数,那么其实已经计算过了中有约数为6的个数了,而且还算了两回,所以这里就要减掉了.而对于而言,它们都已经在计算的时候算过了,可以发现这里新产生的互质对的对数为 对于每个约数而言,前面的系数就是莫比乌斯函数的函数值
代码中先把每个数的约数都预处理出来,然后在主函数中直接枚举,要用输入挂才能卡过去
#include<bits/stdc++.h>
namespace fastIO {
#define BUF_SIZE 100000
//fread -> read1,
bool IOerror = 0;
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if(p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if(pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) {
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
inline void read(int &x) {
char ch;
while(blank(ch = nc()));
if(IOerror)
return;
for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
#undef BUF_SIZE
};
using namespace std;
using namespace fastIO;
typedef long long ll;
const int maxa=200005;
const int maxn=500005;
bool vis[maxn];
int prim[maxn];
int mu[maxn];
int cnt;
void get_mu(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prim[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt && prim[j]*i<=n;j++){
vis[prim[j]*i]=1;
if(i%prim[j]==0) break;
else mu[i*prim[j]]=-mu[i];
}
}
}
int n,q;
int a[maxa];
bool in[maxa];
int c[maxn];
vector<int> g[maxn];
void init(){
get_mu(maxn-1);
for(int i=1;i<maxn;++i){
for(int j=i;j<maxn;j+=i){
g[j].push_back(i);
}
}
}
int main(){
init();
read(n);read(q);
for(int i=1;i<=n;++i) read(a[i]);
ll ans=0;
while(q--){
int id,x;
read(id);
x=a[id];
if(!in[id]){
int num=g[x].size();
for(int i=0;i<num;++i){
int u=g[x][i];
ans+=mu[u]*c[u];
++c[u];
}
}
else{
int num=g[x].size();
for(int i=0;i<num;++i){
int u=g[x][i];
--c[u];
ans-=mu[u]*c[u];
}
}
in[id]=!in[id];
printf("%lld\n",ans);
}
return 0;
}