题解 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\)

\[\begin{aligned} &\sum_{1\leq i\leq n}\sum_{1\leq j\leq n}[\gcd(p_i,p_j)=1]\\ &=\sum_{1\leq i\leq n}\sum_{1\leq j\leq n}\sum_{d|\gcd(p_i,p_j)}\mu(d)\\ &=\sum_{1\leq i\leq n}\sum_{1\leq j\leq n}\sum_{d|\gcd(p_i,p_j)}\mu(d)\\ &=\sum_{1\leq i\leq n}\sum_{1\leq j\leq n}\sum_{d|p_i,d|p_j}\mu(d)\\ &=\sum_d\mu(d)(\sum_{i}[d|p_i])^2=\sum_d\mu(d)cnt_d^2. \end{aligned}\]

请注意这里 \(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;
}
posted @ 2023-01-11 11:18  caijianhong  阅读(36)  评论(0编辑  收藏  举报