题解 ABC230G【GCD Permutation】
problem
给你一个长度为 \(N\) 的排列 \(P=(P_1,P_2,...,P_N)\),你需要求出有多少 \((i,j)\) 满足 \(1\leq i \leq j \leq N,\gcd(i,j)\ne 1,\gcd(P_i,P_j)\ne 1\)。
\(n\leq 200000\)。
以下忽略 \(i=j\) 的 pair。
solution 0
考虑一个简化的问题:你有一些不重复的数,求有多少个有序的 pair 使得 \(\gcd(p_i,p_j)=1\)。这是简单的,使用祖传莫比乌斯反演:\(\mu*I=e\)
请注意这里 \(cnt_d\) 表示 有多少个 \(d\) 的倍数在数列里。
那么我们有一个算法:对于每一个数,枚举这个数的所有因数,统计 \(cnt_d\),统计上式。
如果我们忽略所有的 \(\mu(d)=0\) 的点,它的复杂度是 \(\sum_{1\leq i\leq n}2^{\omega(i)}<O(n^{1.5})\)。好吧这是个无效放缩。。。。
solution 1
我们进化出一个数据结构,支持插入删除,查询集合中有多少个不互质的 pair。
简单的。
solution 2
我们算出所有满足 \(\gcd(i,j)=1\land\gcd(p_i,p_j)\neq1\) 的 pair,用所有 \(\gcd(p_i,p_j)\neq1\) 的 pair 数一减,就是答案了。
我们的方法是:枚举 \(d\),然后拎出所有的下标为 \(d\) 的倍数的 \(p_i\),计算这里面有多少个不互质的 pair。我们假装,它的贡献是 \(\mu(d)\)。
于是这样计算就对了。这本质上是容斥套容斥。
对数据结构更新的次数显然为 \(O(n\ln n)\),那么总的复杂度是 \(O(n^{1.5}\ln n)\)。完全跑不满。
code
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<int N> struct siever{
int pri[N+10],mu[N+10],cnt;
vector<int> dit[200010];
siever():cnt(0){
memset(pri,1,sizeof pri);
pri[0]=pri[1]=0,mu[1]=1;
for(int i=1;i<=N;i++){
if(pri[i]) pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*pri[j]<=N;j++){
pri[i*pri[j]]=0;
if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
else mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=N;i++){
if(!mu[i]) continue;
for(int j=i;j<=N;j+=i) dit[j].push_back(i);
}
}
};
siever<200010> s;
LL c2(int n){return 1ll*n*(n-1)/2;}
template<int N> struct sshwy{//支持插入,删除,查询集合中互质的 pair 数的 sshwy
int cnt[N+10],tot; LL ans;
sshwy():tot(0),ans(0){memset(cnt,0,sizeof cnt);}
//结论:dit[n].size()<=sqrt(n)
void add(int x,int del){
for(int d:s.dit[x]){
if(del==1) ans+=cnt[d]++*s.mu[d];
else ans-=--cnt[d]*s.mu[d];
}
tot+=del;
if(del==1) debug("add(%d,%d),now ans=%lld\n",x,del,ans);
}
LL query(){return c2(tot)-ans;}//查询不互质的 pair 数
};
int n,p[200010];
LL ans=0;
sshwy<200010> cmb;
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
for(int d=2;d<=n;d++){
if(!s.mu[d]) continue;
debug("d=%d\n",d);
for(int i=d;i<=n;i+=d) cmb.add(p[i],1);
ans-=cmb.query()*s.mu[d];
debug("ans+=%lld*%d(%lld)\n",cmb.query(),s.mu[d],cmb.query()*s.mu[d]);
for(int i=d;i<=n;i+=d) cmb.add(p[i],-1);
}
// ans=c2(n)-ans;
//注意这里的总数不是 c2(n),应该是 d=1 的答案,需要满足 p 值不互质。
//d=1 刚好约掉了
for(int i=2;i<=n;i++) if(p[i]>1) ans++;
printf("%lld\n",ans);
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-ABC230G.html