題意:
有一個長度爲n(n<=2e5)的序列,q(q<=2e5)次操作
序列中的每個位置對應一個數ai(1<=ai<=5e5)
每次操作給一個數x,代表挑出的序列中的下標爲x的數
若對應下標的數已經被挑出,那麼就把它放回去
每次操作完,詢問所有挑出的數中互質的對數
剛開始時所有數都沒有被挑出
思路:
剛開始的沒有數被挑出,那麼初始答案爲0。
令num[i] :挑出的數中 i 的個數
如果當前操作的數爲x,那麼對答案的貢獻
如果是挑出則加,放入則減,注意x==1的情況
反演一下貢獻的式子:
轉化枚舉量:
到這裏可以發現,預處理每個數的有效因子,用樹狀數組維護每個d的前綴和
就能在更新答案和前綴和
代碼:
#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
const int N = 5e5+10;
using namespace std;
vector<int>pr,fro[N],E[N];
bool Np[N];
int mu[N];
void init(){
mu[1] = 1;
for(int i=2;i<N;i++){
if(!Np[i]){
mu[i] = -1;
pr.emplace_back(i);
}for(int j=0,k=pr[0]*i;k<N;k=pr[++j]*i){
Np[k] = true;
if(i%pr[j]==0){
mu[k] = 0;
break;
}mu[k] = -mu[i];
}
}for(int i=1;i<N;i++)if(mu[i]){
fro[i].emplace_back(0);
for(int j=i;j<N;j+=i){
E[j].emplace_back(i);
fro[i].emplace_back(0);
}
}
}
inline void add(int d,int x,int val){
int n = fro[d].size();
while(x<n){
fro[d][x]+=val;
x += lowbit(x);
}
}
inline long long query(int d,int x){
long long res = 0;
while(x){
res += fro[d][x];
x -= lowbit(x);
}return res;
}
long long work(int x){
long long res = 0;
for(int i=0;i<E[x].size();i++){
int d = E[x][i];
long long tmp = 0;
tmp += query(d,500000/d);
res += tmp * mu[d];
}return res;
}
void oper(int x,int val){
for(int i=0;i<E[x].size();i++){
int d = E[x][i];
add(d,x/d,val);
}
}
int A[N];
bool has[N];
int main()
{
init();
int n,q,x;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
}long long ans = 0;
while(q--){
scanf("%d",&x);
long long num = work(A[x]);
if(!has[x]){
ans += num;
oper(A[x],1);
}else {
if(A[x]==1)num--;
ans -= num;
oper(A[x],-1);
}has[x] = !has[x];
printf("%I64d\n",ans);
}return 0;
}