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
#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;
}

你可能感兴趣的:(算法,数据结构,c++)