CF1986G2 Permutation Problem (Hard Version) 题解

· · 题解

嘻嘻,来写一篇紫题题解。

首先,由于要求的是 i \times j \mid p_i \times p_j,所以考虑对每个 \dfrac{p_i}{i} 约分,变成 \dfrac{a_i}{b_i}。塞进 vector 里让它们一一对应起来。

约分以后 i,j 两个下标满足要求就当且仅当 b_i \mid a_j 并且 b_j \mid a_i 了。应该很显然吧。

枚举 b_i 的所有倍数尝试作为 a_j。尽管两个循环但是调和级数,这时间复杂度很显然是 O(n \log n) 的,而且可能还不到。

知道 a_j 就有待选 b_j 了,我们把这些 b_j 扔进一个桶里。

同样的,一开始我们知道 b_i,所以就有符合条件的 a_i。枚举这些 a_i 分别对应的因数 b_j,利用刚才桶里存的那些待选 b_j 的信息,累加答案。

然后就结束了。

最后输出的时候记得 \div 2,因为在我们的算法中 i < ji > j 的情况都会被统计进去,可题目只需要 i < j 一种答案。

最后附上 [AC](https://codeforces.com/contest/1986/submission/325250306) 代码: ```cpp #include<bits/stdc++.h> using namespace std; const int N = 5e5+5; int T,n,p[N],cnt[N];long long ans;vector<int> a[N],b[N],k[N]; int GCD(int x,int y){if(x<y)swap(x,y);return (!y?x:GCD(y,x%y));} int main(){ cin>>T; for(int i=1;i<=500000;i++) for(int j=i;j<=500000;j+=i)k[j].push_back(i); while(T--){ cin>>n;ans=0; for(int i=1;i<=n;i++){ cin>>p[i];long long g=GCD(p[i],i),x=p[i]/g,y=i/g; a[y].push_back(x),b[x].push_back(y);ans-=(y==1); } for(int x=1;x<=n;x++){ for(int u=x;u<=n;u+=x)for(int v:b[u])cnt[v]++; for(int y:a[x])for(int u:k[y])ans+=cnt[u]; for(int u=x;u<=n;u+=x)for(int v:b[u])cnt[v]--; } cout<<(ans/2)<<"\n"; for(int i=1;i<=n;i++)a[i].clear(),b[i].clear(); } return 0; } ``` 超短,对吧? 如果本篇题解对你有帮助的话,麻烦你点一个小小的赞,真是太谢谢啦!