題意
給定一個長度爲 的數組,。維護一個集合,一開始集合爲空。一共有 次操作,每次操作給定一個下標 ,如果 已經在集合中,則將 從集合中刪除,否則將 加入集合。注意,集合允許有重複的數字。
問每次操作完之後,集合中互質的數字有多少對。
思路
此題算是一道比較經典的容斥題,也可以用莫比烏斯函數來求解,但二者本質是一樣的。
首先比較容易想到的是,往集合中加一個數 ,則答案加上集合中與 互質數的個數;從集合中刪除一個數 ,則答案減去集合中與 互質數的個數,這種做法的複雜度是 。
想要進一步優化複雜度,則需要降低求集合中與 互質數個數的複雜度。我們將求互質轉爲求不互質,即:
又因爲如果 與 不互質,則必存在至少一個質數 ,滿足 且 ,因此我們假設 的質因子有 , 表示集合中有多少個數有 這個質因子,則可以根據容斥原理得到:
所有我們可以先求出每個數的質因子集合,然後用 枚舉質因子子集,每當得到一個子集,則將子集中的數字全部乘起來,同時更新 數字與容斥答案,具體細節見代碼。
代碼
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = a; i <= b; i++)
typedef long long ll;
const int N = 5e5+1000;
using namespace std;
int n,q,a[N],vis[N],flag[N],C[N];
ll ans, tmp, cot;
vector<int> prime[N];
void init() {
scanf("%d%d",&n,&q);
rep(i,1,n) scanf("%d",&a[i]);
for(int i = 2; i <= 5e5; i++){
if(vis[i]) continue;
for(int j = i; j <= 5e5; j += i) {
vis[j] = 1;
prime[j].push_back(i);
}
}
}
void dfs(int now, int num, int cnt, int mul, int op) {
if(num == prime[now].size()) return;
if(op == 1) {
// 加入 now
tmp += cnt*C[mul*prime[now][num]];
C[mul*prime[now][num]]++;
}
else {
// 減去 now
C[mul*prime[now][num]]--;
tmp += cnt*C[mul*prime[now][num]];
}
dfs(now, num + 1, cnt, mul, op);
dfs(now, num + 1, cnt*(-1), mul*prime[now][num], op);
}
int main()
{
init();
while(q--) {
int pos; scanf("%d",&pos);
flag[pos] ^= 1;
tmp = 0;
if(flag[pos]) {
dfs(a[pos], 0, 1, 1, 1);
ans += cot - tmp;
cot++;
}
else {
dfs(a[pos], 0, 1, 1, -1), cot--;
ans -= cot - tmp;
}
printf("%lld\n", ans);
}
return 0;
}