codeforces round 911 D - Small GCD

分析

  • ∑ i = 1 n ∑ j = i + 1 n ∑ k = j + 1 n = f ( a i , a j , a k ) a i , a j \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{k=j+1}^{n}=f(a_i,a_j,a_k)\quad a_i,a_j i=1nj=i+1nk=j+1n=f(ai,aj,ak)ai,aj 是较小的两数
  • 易得简单做法排序后通过枚举前两个数结合 G C D GCD GCD 计算,但是复杂的为 n 2 l o g n n^2logn n2logn T E L TEL TEL

思路

  • 因数分解:预处理出可能会用到的所有数的所有因数。因数分解比较耗时,所以在每个 c a s e case case 里再去处理是不现实的。
  • 试想在排序后我们枚举 a i    a j a_i\;a_j aiaj 计算两者 G C D GCD GCD 的贡献,计算的就是因数对答案的贡献不过是最大公约数 。我们可以用一个 f o r for for 遍历数组,每到一个数就记录该数所有出现过的因数对答案的贡献最大公约数对答案才有贡献,但我们不计算最大公约数,我们记录每一个约数的贡献,其中一定包括最大公约数,而出现过就保证了是公约数 s u m [ x ] + = c n t [ x ] ∗ ( n − i )    n − i sum[x]{+}{=}cnt[x]*(n-i)\;n-i sum[x]+=cnt[x](ni)ni 是距离,应题目。记录之后需 + + c n t [ x ] {++}cnt[x] ++cnt[x] 记录出现过的次数
  • 容斥:考虑 x x x 就是最大公约数,那所有 x x x 的倍数作为最大公约数时的贡献都应该减去。因为 x x x x x x 的倍数是同在一个因数分解的桶中的,而我们 f o r for for 遍历时 + + c n t [ x ] {++}cnt[x] ++cnt[x] 每一个因数。
    Think Twice, Code Once
#include<bits/stdc++.h>
#define il inline
#define get getchar
#define put putchar
#define is isdigit
#define re register
#define int long long
#define dfor(i,a,b) for(re int i=a;i<=b;++i)
#define dforr(i,a,b) for(re int i=a;i>=b;--i)
#define dforn(i,a,b) for(re int i=a;i<=b;++i,put(10))
#define mem(a,b) memset(a,b,sizeof a)
#define memc(a,b) memcpy(a,b,sizeof a)
#define pr 114514191981
#define gg(a) cout<<a,put(32)
#define INF 0x7fffffff
#define tt(x) cout<<x<<'\n'
#define ls i<<1
#define rs i<<1|1
#define la(r) tr[r].ch[0]
#define ra(r) tr[r].ch[1]
#define lowbit(x) (x&-x)
using namespace std;
typedef unsigned int ull;
int read(void)
{
    re int x=0,f=1;re char c=get();
    while(!is(c)) (f=c==45?-1:1),c=get();
    while(is(c)) x=(x<<1)+(x<<3)+(c^48),c=get();
    return x*f;
}
void write(int x)
{
    if(x<0) x=-x,put(45);
    if(x>9) write(x/10);
    put((x%10)^48);
}
#define writeln(a) write(a),put(10)
#define writesp(a) write(a),put(32)
#define writessp(a) put(32),write(a)
const int N=1e5+10,M=3e4+10,SN=1e4+10,mod=19930726;
int n,ans,a[N],cnt[N],sum[N];
vector<int > ve[N];
signed main()
{
    int T=read();
    dfor(i,1,N)
        for(re int j=i;j<N;j+=i) ve[j].push_back(i);
    while(T--)
    {
        mem(cnt,0),mem(sum,0),ans=0;
        n=read();
        dfor(i,1,n) a[i]=read();
        sort(a+1,a+n+1);
        dfor(i,1,n)
        {
            int len=ve[a[i]].size()-1;
            dfor(j,0,len)
            {
                sum[ve[a[i]][j]]+=cnt[ve[a[i]][j]]*(n-i);
                ++cnt[ve[a[i]][j]];
            }
        }
        dforr(i,a[n],1)
        {
            for(re int j=i<<1;j<N;j+=i) sum[i]-=sum[j];
            ans+=sum[i]*i;
        }
        writeln(ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Heredy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值