2019 Multi-University Training Contest 1 - 1011 - Function - 数论

http://acm.hdu.edu.cn/showproblem.php?pid=6588

新学到了一个求n以内与m的gcd的和的快速求法。也就是下面的S1。


①求:

$ \sum\limits_{i=1}^{n}gcd(m,i) $

②枚举d:

$ \sum\limits_{d|m} d \sum\limits_{i=1}^{n} [gcd(m,i)==d] $

③显然:

$ \sum\limits_{d|m} d \sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor} [gcd(\frac{m}{d},i)==1] $

到这一步已经可以递归求了,琪琪说是 \(O(n^{\frac{3}{4}})\) ,不过题解可以继续往下。

④为了方便直接考虑 $ \sum\limits_{i=1}^{n} [gcd(m,i)==1] $ ,反演(大概):

$ \sum\limits_{i=1}^{n} \sum\limits_{d|gcd(m,i)} \mu(d) $

⑤交换一下顺序,枚举d,很显然n以内的d的倍数都会贡献一个mu(d):

$ \sum\limits_{d|m} \mu(d) \lfloor\frac{n}{d}\rfloor $


下面的是根据题解的实现,不过是__int64的版本。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int64 lll;

const int mod = 998244353;
const int MAXN = 10000000;

int phi[MAXN + 1];
int pri[MAXN + 1], pritop;
bool notpri[MAXN + 1];

void sieve() {
    int n = MAXN;
    pri[1] = phi[1] = 1;
    for(int i = 2; i <= n; i++) {
        if(!pri[i])
            pri[++pritop] = i, phi[i] = i - 1;
        for(int j = 1, tmp; j <= pritop && (tmp = i * pri[j]) <= n; j++) {
            pri[tmp] = 1;
            if(i % pri[j])
                phi[tmp] = phi[i] * phi[pri[j]];
            else {
                phi[tmp] = phi[i] * pri[j];
                break;
            }
        }
    }
}

ll S1(lll n, int m) {
    //sigma gcd(i,m) [1,n]
    ll res = 0;
    for(int T = 1; T * T <= m; ++T) {
        if(!(m % T)) {
            res += (n / T) * phi[T];
            if(T * T != m) {
                res += (n / (m / T)) * phi[(m / T)];
            }
        }
    }
    res %= mod;
    return res;
}

ll qpow(ll x, int n) {
    ll res = 1;
    while(n) {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

const int inv2 = qpow(2ll, mod - 2);
const int inv6 = qpow(6ll, mod - 2);

ll sigma1(ll x) {
    return x * (x + 1ll) % mod * inv2 % mod;
}

ll sigma2(ll x) {
    return x * (x + 1ll) % mod * (2ll * x + 1ll) % mod * inv6 % mod;
}

ll S2_1(int r, int T) {
    int c = r / T;
    ll res = 0;
    res += 3ll * T * sigma2(c);
    res += 3ll * sigma1(c);
    res += c;
    res %= mod;
    return res;
}

ll S2(int r) {
    ll res = 0;
    for(int T = 1; T <= r; ++T) {
        res += 1ll * phi[T] * S2_1(r, T) % mod;
    }
    res %= mod;
    return res;
}

ll S0(lll n) {
    lll i, i3;
    for(i = 1;; ++i) {
        lll tmp = i * i * i;
        if(tmp > n) {
            --i;
            break;
        } else
            i3 = tmp;
    }
    ll res = 0;
    res += S1(n, i) - S1(i3 - 1, i);
    res += S2(i - 1);
    res = (res % mod + mod) % mod;
    return res;
}

inline lll read() {
    lll x = 0;
    char c;
    do {
        c = getchar();
    } while(c < '0' || c > '9');
    do {
        x = (x << 3) + (x << 1) + c - '0';
        c = getchar();
    } while(c >= '0' && c <= '9');
    return x;
}

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    sieve();
    int T;
    cin >> T;
    lll n;
    while(T--) {
        n = read();
        cout << S0(n) << endl;
    }
}

其实具体的思路还是要先分成两部分来算,但是我当时不会计算这个S1导致T了。

猜你喜欢

转载自www.cnblogs.com/Yinku/p/11229435.html