Luogu 4900 食堂

一道把很多东西放在一起的练手题。

$$\sum_{i = A}^{B}\sum_{j = 1}^{i}\left \{ \frac{i}{j} \right \} = \sum_{i = A}^{B}\sum_{j = 1}^{i}\frac{n - \left [ \frac{n}{j}\right] * j}{i} = \sum_{i = A}^{B}(i * \sum_{j = 1}^{i}inv(j) - \sum_{j = 1}^{i}\left \lfloor \frac{n}{j} \right \rfloor)$$

这样子把里面的$(i * \sum_{j = 1}^{i}inv(j) - \sum_{j = 1}^{i}\left \lfloor \frac{n}{j} \right \rfloor)$算出来做个前缀和就好了。

前面的那个东西可以$O(n)$递推然后前缀和算出来,而后面的那个东西其实就是$\sum_{i = 1}^{n}d(i)$($d(i)$表示$i$的约数个数)。

证明:

$$\sum_{i = 1}^{n}d(i) = \sum_{i = 1}^{n}\sum_{j = 1}^{i}\left [ j| i\right] = \sum_{j = 1}^{n}\sum_{i = 1}^{n}\left [ j| i\right] = \sum_{i = 1}^{n}\left \lfloor \frac{n}{i} \right \rfloor$$

这样子线性筛之后前缀和就做完了。

记得数组开少一点……我就是这样MLE了一次。

时间复杂度$O(n)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 1e6 + 5;
const int Maxn = 1e6;
const ll P = 998244353LL;

int testCase, pCnt = 0, pri[N], low[N];
ll inv[N], d[N], h[N]; 
bool np[N];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48; 
    X *= op;
}

template <typename T>
inline void inc(T &x, T y) {
    x += y;
    if(x >= P) x -= P;
}

inline void sieve() {
    d[1] = 1;
    for(int i = 2; i <= Maxn; i++) {
        if(!np[i]) 
            pri[++pCnt] = i, low[i] = i, d[i] = 2;
        for(int j = 1; i * pri[j] <= Maxn && j <= pCnt; j++) {
            np[i * pri[j]] = 1;
            if(i % pri[j] == 0) {
                low[i * pri[j]] = pri[j] * low[i];
                if(low[i] == i) d[i * pri[j]] = d[i] + 1;
                else d[i * pri[j]] = d[i / low[i]] * d[pri[j] * low[i]];
                break;    
            }
            low[i * pri[j]] = pri[j]; 
            d[i * pri[j]] = d[i] * d[pri[j]]; 
        }
    }
    
    for(int i = 1; i <= Maxn; i++) inc(d[i], d[i - 1]);
}

int main() {
    sieve();
    inv[1] = 1LL;
    for(int i = 2; i <= Maxn; i++) inv[i] = inv[P % i] * (P - P / i) % P;
    for(int i = 1; i <= Maxn; i++) inc(inv[i], inv[i - 1]);
    
    for(int i = 1; i <= Maxn; i++) h[i] = (1LL * i * inv[i] % P - d[i] + P) % P;
    for(int i = 1; i <= Maxn; i++) inc(h[i], h[i - 1]);
    
    read(testCase);
    for(int l, r; testCase--; ) {
        read(l), read(r);
        printf("%lld\n", (h[r] - h[l - 1] + P) % P);
    }
    
    return 0;
}
View Code

 

posted @ 2018-12-12 20:28  CzxingcHen  阅读(132)  评论(0编辑  收藏  举报