今天的NOIP模拟中出现了一道奇怪的题目。。。它让我明白了考试的时候脑洞要大。。。做不出就不要大意地先反演一波
对于做过了反演题的人来说,提到了gcd、计数还看不出它和反演有关,是应该要被打的。。。所以我要记录下来引以为戒。
gcd
【题目描述】
有 n 个正整数 x1~xn,初始时状态均为未选。
有 m 个操作,每个操作给定一个编号 i,将 xi 的选取状态取反。
每次操作后,你需要求出选取的数中有多少个互质的无序数对。
【输入数据】
第一行两个整数 n,m。第二行 n 个整数 x1~xn。接下来 m 行每行
一个整数。
【输出数据】
m 行,每行一个整数表示答案。
(1<=n,m<=100000,1<=x[i]<=500000)
设f[k]=sum(i,j) (gcd(x[i],x[j])==k) ,则f[k]=sum(miu(d)*sum(i,j)); (d*k | gcd(x[i],x[j]))
设g[k]=sum(i,j) (k|gcd(x[i],x[j])),则f[k]=sum(miu(d)*g(d*k));
设s[k]=sum(i) (k|x[i]),则g[k]=s[k]*(s[k]-1)/2;所以ans=f[1]=sum(miu(d)*g(d));更新时根号维护s[i]->维护g[i]->维护f[1]即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline int read(){
int x=0; int w=0; char ch=0;
while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return w? -x:x;
}
const int maxn=500005;
int miu[maxn],prime[maxn],N,n,m,x[maxn],a[maxn];
LL g[maxn],s[maxn];
bool pd[maxn];
void Euler(int n){
miu[1]=1; int num=0;
rep(i,2,n){
if (!pd[i]){ prime[++num]=i; miu[i]=-1; }
rep(j,1,num){
LL t=i*1LL*prime[j]; if (t>n) break;
pd[t]=1; miu[t]=(!(i%prime[j]))? 0:((-1)*miu[i]);
if (!(i%prime[j])) break;
}
}
}
int main(){
//freopen("gcd.in","r",stdin);
//freopen("gcd.out","w",stdout);
n=read(); m=read(); rep(i,1,n){ x[i]=read(); N=max(N,x[i]); }
Euler(N); LL ans=0;
rep(i,1,m){
int k=read(); a[k]^=1; int tmp=(a[k])? 1:(-1);
rep(j,1,floor(sqrt(x[k])))
if (!(x[k]%j)) {
s[j]+=tmp; ans-=miu[j]*g[j]; g[j]=s[j]*(s[j]-1)/2; ans+=miu[j]*g[j];
if (x[k]!=j*j){ s[x[k]/j]+=tmp; ans-=miu[x[k]/j]*g[x[k]/j];
g[x[k]/j]=s[x[k]/j]*(s[x[k]/j]-1)/2; ans+=miu[x[k]/j]*g[x[k]/j]; }
}
printf("%lld\n",ans);
}
return 0;
}