专栏文章

[题解] AT_abc230_g [ABC230G] GCD Permutation

AT_abc230_g题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miq4boa7
此快照首次捕获于
2025/12/03 22:44
3 个月前
此快照最后确认于
2025/12/03 22:44
3 个月前
查看原文
大家好,我不会莫反,我是暴力老哥。
考虑给定序列 aagcd(ai,aj)1\gcd(a_i,a_j)\ne 1(i,j)(i,j) 数量,这是容易做的。我们先对于每个值统计出 gcd(ai,aj)\gcd(a_i,a_j) 是其倍数的数量,然后枚举倍数容斥一下即可。
我们考虑本题就相当于做两次上面的东西套起来:先枚举一个值 xx,并计算 xgcd(i,j)x|\gcd(i,j) 的数量,然后容斥。计算这个就是将每个满足 xix|ipip_i 提出来做上面的东西。
但是直接做跑得很慢。考虑对每个 xx 计算的过程。我们先用一个数组 dd 记录所有 xix|ipip_i 的因数 yy 并统计每个因数出现次数,之后将 dd 排序,从大到小计算对数,然后枚举 yy 的倍数并减去倍数的方案数。
我们发现枚举倍数的这部分有两种方法:直接枚举 yy 的倍数,或者枚举在 dd 中比 yy 索引大的位置,并选取其中的倍数。当然两种都无法直接通过。但两者各有优劣,第一种对于较大的 yy 运算量很小,而 yy 较小时很慢,但第二种中可取的范围总是比较有限。所以考虑每次枚举倍数时选择两种中运算量小的一种。
不会算复杂度,但是没怎么卡常就过了,时间 3.9s3.9\text s。第二种方法中取模比较慢,调一下参可能更快。

代码

CPP
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define SZ(x) (int)(x).size()
using namespace std;
const int N=2e5+5;
int n,a[N],d[N];
ll f[N],g[N],ans;
basic_string<int> ve[N];
signed main(){
	ios::sync_with_stdio(0);cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=2;i<=n;i++)
		for(int j=i;j<=n;j+=i)
			ve[j].pb(i);
	for(int i=2;i<=n;i++){
		int t=0;
		for(int j=i;j<=n;j+=i)
			for(int v:ve[a[j]])
				if(!g[v]++)d[++t]=v;
		sort(d+1,d+t+1);
		for(int j=t;j;j--){
			g[d[j]]=g[d[j]]*(g[d[j]]+1)/2;
			if(n/d[j]<t-j)
				for(int k=d[j]*2;k<=n;k+=d[j])
					g[d[j]]-=g[k];
			else for(int k=j+1;k<=t;k++)
				if(d[k]/d[j]*d[j]==d[k])g[d[j]]-=g[d[k]];
			f[i]+=g[d[j]];
		}
		for(int j=1;j<=t;j++)g[d[j]]=0;
	}
	for(int i=n;i>1;i--){
		for(int j=i*2;j<=n;j+=i)
			f[i]-=f[j];
		ans+=f[i];
	}
	cout<<ans<<'\n';
	return 0;
}
(逃

评论

0 条评论,欢迎与作者交流。

正在加载评论...