GCD SUM

GCD Sum

原题链接:https://www.codechef.com/problems/GCDSUM

题意

给定一个\(n\times m\)的矩阵,每次挑选一个\(k \in [2,n]\),然后挑选\(k\)行,再在这\(k\)行里分别挑\(1\)个元素,计算出他们的\(\gcd\),再计算所有挑选方法所得的\(\gcd\)的和。

题解

如果真按题目描述的那样来模拟着做,将会非常麻烦,于是转化思路,所有的\(\gcd\)只会在\([1,10^5]\)区间内,我们计算每个值被算了多少次即可。那如何计算出每个值被计算了多少次呢?

我们设立一个二维数组\(cnt\)\(cnt[i][j]\)代表了数字\(j\)在第\(i\)行出现的次数。再设立一个数组\(cc\)\(cc[i][j]\)代表数字\(j\)及其它的倍数在第\(i\)行出现的次数。对于数字\(j\),他被计算的次数需要稍微推导一下:

对于一个\(n=4\)的情况,我们假设在第\(1,2,3,4\)\(j\)及其它的倍数出现的次数为\(a,b,c,d\),那么数字\(j\)被计算的次数就为:

\(k=2:ab+ac+ad+bc+bd+cd\)

\(k=3:abc+abd+bcd\)

\(k=4:abcd\)

利用一下高中学过的知识就可判断出他是\((1+a)(1+b)(1+c)(1+d)\)的展开式再减去\(a+b+c+d+1\)

但还没完,我们这样算其实是算多了的,\(\gcd=kj,k=2,3,4,...\)的情况也被算进去了,减掉即可。这样一来,数字\(j\)对答案的贡献就是\(j\times \sum cc[i][j]-\sum_{t=2}ans[tj]\)\(ans\)数组用来记录答案,\(ans_j\)就是数字\(j\)对答案的贡献。

\(cc\)数组的第二维每次循环只用到了一次,所以可以只需要开一维的即可。

时间复杂度:\(O(nM\log{M})\)

AC代码

#pragma GCC optiminze(2)
#include "bits/stdc++.h"

#define IO ios::sync_with_stdio(NULL)
#define sc(z) scanf("%d", &(z))
#define _ff(i, a, b) for (ll i = a; i <= b; ++i)
#define _rr(i, a, b) for (ll i = b; i >= a; --i)
#define _f(i, a, b) for (ll i = a; i < b; ++i)
#define _r(i, a, b) for (ll i = b - 1; i >= a; --i)
#define mkp make_pair
#define endl "\n"
#define lowbit(x) x&(-x)
#define pb push_back

using namespace std;
typedef double db;
typedef long long ll;
typedef long double ld;

const int N = 20 + 5;
const int M = 1e5 + 5;
const ll mod = 1e9 + 7;
const double eps = 1e-9;
const double pi = acos(-1.0);

ll a[N][M], cnt[N][M], dp[M], cc[N];

void solve() {
    int n, m; cin >> n >> m;
    _ff(i,1,n)_ff(j,1,m)cin>>a[i][j];

    ll ans = 0;

    _ff(i,1,n)_ff(j,1,m)cnt[i][a[i][j]]++;

    _r(i,1,M){
        _ff(l,1,n){
            cc[l]=0;
            for (int j=i;j<M;j+=i)cc[l]+=cnt[l][j];//埃氏筛筛出第l行所有i的倍数出现的次数
        }
        
        dp[i]=1;
        _ff(j,1,n)dp[i]=dp[i]*(1+cc[j]) % mod;
        _ff(j,1,n)dp[i]=(dp[i]-cc[j]+mod)%mod;
        dp[i]=(dp[i]-1+mod)%mod;
        
        for(int j=i+i;j<M;j+=i)dp[i]=(dp[i]-dp[j]+mod)%mod;//减去所有gcd为i的倍数的答案
        ans=(ans+i*dp[i]%mod)%mod;
    }
    cout<<ans<<endl;
}

int main() {
    IO;

    solve();

    return 0;
}
posted @ 2021-08-09 11:56  xDaniel  阅读(90)  评论(0编辑  收藏  举报