CF1780F Three Chairs

题目描述

One day Kira found n n n friends from Morioh and decided to gather them around a table to have a peaceful conversation. The height of friend i i i is equal to a i a_i ai . It so happened that the height of each of the friends is unique.

Unfortunately, there were only 3 3 3 chairs in Kira’s house, and obviously, it will not be possible to seat all friends! So, Kira has to invite only 3 3 3 of his friends.

But everything is not so simple! If the heights of the lowest and the tallest of the invited friends are not coprime, then the friends will play tricks on each other, which will greatly anger Kira.

Kira became interested, how many ways are there to choose 3 3 3 of his friends so that they don’t play tricks? Two ways are considered different if there is a friend invited in one way, but not in the other.

Formally, if Kira invites friends i i i , j j j , and k k k , then the following should be true: gcd ⁡ ( min ⁡ ( a i , a j , a k ) , max ⁡ ( a i , a j , a k ) ) = 1 \gcd(\min(a_i, a_j, a_k), \max(a_i, a_j, a_k)) = 1 gcd(min(ai,aj,ak),max(ai,aj,ak))=1 , where gcd ⁡ ( x , y ) \gcd(x, y) gcd(x,y) denotes the greatest common divisor (GCD) of the numbers x x x and y y y .

Kira is not very strong in computer science, so he asks you to count the number of ways to invide friends.

题意简述

给一个长度为 n n n 的数组 a a a,数字两两不同。求有多少个三元组 ( a i , a j , a k ) (a_i,a_j,a_k) (ai,aj,ak),满足 i < j < k ii<j<k gcd ⁡ ( a i , a k ) = 1 \gcd(a_i,a_k)=1 gcd(ai,ak)=1

题解

首先给 a a a 数组从小到大排序,容易想到枚举 i , k i,k i,k,若 gcd ⁡ ( a i , a k ) = 1 \gcd(a_i,a_k)=1 gcd(ai,ak)=1,则对答案有 ( k − i − 1 ) (k-i-1) (ki1) 的贡献。

由此可得 a n s = ∑ k = 1 n ∑ i = 1 k − 1 [ gcd ⁡ ( a i , a k ) = 1 ] ( k − i − 1 ) ans=\sum\limits_{k=1}^n\sum\limits_{i=1}^{k-1}\left[\gcd(a_i,a_k)=1\right](k-i-1) ans=k=1ni=1k1[gcd(ai,ak)=1](ki1)

根据莫比乌斯函数的性质 ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum\limits_{d\mid n}\mu(d)=[n=1] dnμ(d)=[n=1],有 ∑ k = 1 n ∑ i = 1 k − 1 ∑ d ∣ gcd ⁡ ( a i , a k ) μ ( d ) ( k − i − 1 ) \sum\limits_{k=1}^n\sum\limits_{i=1}^{k-1}\sum\limits_{d\mid\gcd(a_i,a_k)}\mu(d)(k-i-1) k=1ni=1k1dgcd(ai,ak)μ(d)(ki1)

因为 d ∣ gcd ⁡ ( a i , a k ) d\mid\gcd(a_i,a_k) dgcd(ai,ak),所以 d ∣ a i ∧ d ∣ a k d\mid a_i\land d\mid a_k daidak,二者是等价的。

先枚举 d d d,有 ∑ d = 1 a n μ ( d ) ∑ d ∣ a k ∑ d ∣ a i ∧ i < k ( k − i − 1 ) \sum\limits_{d=1}^{a_n}\mu(d)\sum\limits_{d\mid a_k}\sum\limits_{d\mid a_i\land id=1anμ(d)dakdaii<k(ki1)

为了方便,继续化简式子

∑ d = 1 a n μ ( d ) ∑ d ∣ a k ( n u m ⋅ ( k − 1 ) − ∑ d ∣ a i ∧ i < k i ) \sum\limits_{d=1}^{a_n}\mu(d)\sum\limits_{d\mid a_k}\left(num\cdot(k-1)-\sum\limits_{d\mid a_i\land id=1anμ(d)dak num(k1)daii<ki

n u m num num 表示满足 d ∣ a i ∧ i < k d\mid a_i\land idaii<k 的数量。

观察这个式子, μ \mu μ 可以 O ( a n ) O(a_n) O(an) 预处理,对于后半部分的式子,我们可以枚举 d d d 的倍数 x x x,若 x x x 是某个 a k a_k ak 就可以进行计算。

因为 d ∣ a k , d ∣ a i d\mid a_k,d\mid a_i dak,dai ,又 i < k ii<k ,所以 a i a_i ai 一定是之前访问过的。

这样, n u m num num 可以快速求得,用 s u m sum sum 记录前面满足 d ∣ a i ∧ i < k d\mid a_i\land idaii<k i i i 的和,遇到 d ∣ a k d\mid a_k dak 时先算现在的贡献,再 n u m ← n u m + 1 , s u m ← s u m + k num\gets num+1,sum\gets sum+k numnum+1,sumsum+k

总的时间复杂度为 O ( n log ⁡ n + a n ln ⁡ a n ) O(n\log n+a_n\ln a_n) O(nlogn+anlnan)

代码

#include
using namespace std;
typedef long long ll;
const int N=3e5+1;
int pd[N],mu[N],p[N],cnt,a[N],n,t[N];
ll ans;
int main()
{
    pd[1]=mu[1]=1;
    for(int i=2;i<N;i++){
        if(!pd[i]) p[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&i*p[j]<N;j++){
            pd[i*p[j]]=1;
            if(i%p[j]==0) break;
            mu[i*p[j]]=-mu[i];
        }
    }
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++) t[a[i]]=i;//记录每个a_i的下标
    for(int d=1;d<=a[n];d++){
        ll Sum=0,num=0,sum=0;
        for(int x=d;x<=a[n];x+=d){//枚举d的倍数x
            if(t[x]){//t_x就是当前的k
                Sum+=num*(t[x]-1)-sum;//加上当前的贡献
                num++,sum+=t[x];//处理num,sum,为下次作准备
            }
        }
        ans+=mu[d]*Sum;
    }
    cout<<ans;
}

你可能感兴趣的:(c++)