Luogu 4449 于神之怒加强版

挺套路的题,然而一开始还是想错了……

$\sum_{i = 1}^{n}\sum_{j = 1}^{m}gcd(i, j) ^ {k} = \sum_{T = 1}^{min(n, m)}\left \lfloor \frac{n}{T} \right \rfloor \left \lfloor \frac{m}{T} \right \rfloor\sum_{d | T}\mu (\frac{T}{d}) * d^{k}$

我们设$h(i) =\sum_{d | T}\mu (\frac{T}{d}) * d^{k} $, 那么原式化为$ \sum_{T = 1}^{min(n, m)}\left \lfloor \frac{n}{T} \right \rfloor \left \lfloor \frac{m}{T} \right \rfloor h(i)$

我们做出$h(i)$的前缀和之后就可以整除分块了。

发现$h(i)$是一个积性函数,可以线性筛,具体筛法如下:

1、$h(1) = 1$

2、$h(p) = p^{k} - 1$($p$是一个质数)

3、考虑线性筛中的过程,$i * pri_{j}$被它的最小因子也就是$pri_{j}$筛掉,那么当$i$不是$pri_{j}$的倍数的时候,也就是说$h(i * pri_{j}) = h(i) * h(pri_{j})$。

  而当$pri_{j} | i$的时候,考虑分两步:($ i = \prod_{j = 1}^{m}p_{j}^{c_{j}}$)

  记$low_{i} =$ $i$的最小质因子被$i$包含的最大幂次积(即$p_{1} ^ {c_{1}}$)

  a、我们假设$low_{i}$不等于$i$,这句话等价于$i$不是一个质数的倍数,所以$\frac{i}{low_{i}}$ 与 $pri_{j} * low_{i}$互质,那么直接乘上就好了。

  b、当$low_{i}$等于$i$的时候,我们考虑一下$h(i)$函数的定义,展开式子之后发现$h(i) = pri_{j} ^ {mk} - pri_{j} ^ {(m - 1)k}$,而$h(i  * pri_{j}) = pri_{j} ^ {(m + 1)k} - pri_{j} ^ {mk}$,那么就相当于$h(i * pri_{j}) = h(i) * pri_{j}^{k}$。

时间复杂度$O(maxNlogmaxN + T\sqrt{n})$。

大时限题还感觉跑得挺快的。

Code:

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

const int N = 5e6 + 5;
const ll P = 1e9 + 7;

int testCase, pCnt = 0, pri[N];
ll k, h[N], low[N], sum[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;
}

inline ll pow(ll a, ll b) {
    ll res = 1LL;
    for(; b > 0; b >>= 1) {
        if(b & 1) res = res * a % P;
        a = a * a % P;
    }
    return res;
}

inline void sieve() {
    h[1] = 1LL;
    for(int i = 2; i < N; i++) {
        if(!np[i]) {
            pri[++pCnt] = i;
            low[i] = (ll)i;
            h[i] =
             (pow(i, k) - 1LL + P) % P;
        }
        for(int j = 1; j <= pCnt && pri[j] * i < N; j++) {
            np[i * pri[j]] = 1;
            if(i % pri[j] == 0) {
                low[i * pri[j]] = low[i] * pri[j];
                if(low[i] == i) h[i * pri[j]] = h[i] * pow(pri[j], k) % P;
                else h[i * pri[j]] = h[i / low[i]] * h[low[i] * pri[j]] % P;
                break;
            }
            low[i * pri[j]] = pri[j];
            h[i * pri[j]] = h[i] * h[pri[j]] % P;
        }
    }
    
    
/*    for(int i = 1; i < 20; i++)
        printf("%lld ", phi[i]);
    printf("\n");   
    for(int i = 1; i < 30; i++)
        printf("%lld ", h[i]);
    printf("\n");   */
    
/*    for(int i = 1; i <= 20; i++)
        printf("%lld ", h[i] % P);   */
    
    for(int i = 1; i < N; i++) 
        sum[i] = (sum[i - 1] + h[i] % P + P) % P;
}

inline ll min(ll x, ll y) {
    return x > y ? y : x;
}

int main() {
    read(testCase), read(k);
    sieve();
    for(ll n, m; testCase--; ) {
        read(n), read(m);
        ll ans = 0LL, rep = min(n, m);
        for(ll l = 1, r; l <= rep; l = r + 1) {
            r = min((n / (n / l)), (m / (m / l)));
            ans = (ans + (sum[r] - sum[l - 1] + P) % P * (n / l) % P * (m / l) % P) % P;
        }
        printf("%lld\n", ans);
    } 
    return 0;
}
View Code

 

posted @ 2018-08-23 13:37  CzxingcHen  阅读(203)  评论(2编辑  收藏  举报