題目鏈接
題意
求
(題面上寫
十分感謝WuBaizhe,不然我就一直RE死不瞑目了…
莫比烏斯反演這塊也是一篇一篇看着WuBaizhe的blog學的,對初學者十分友好,每篇的推導都很詳細,非常感謝原Po)
初級版(280ms)
推導
令
預處理
Code
#include <cstdio>
#include <iostream>
#define maxn 1000010
#define maxm maxn + 10
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7;
bool check[maxm];
int prime[maxm], kas;
LL h[maxm], pre[maxm], sum5[maxm], sum2[maxm];
LL poww(LL a, LL b) {
LL ret = 1;
while (b) {
if (b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
void init() {
h[1] = 1; int tot = 0;
for (int i = 2; i <= maxn; ++i) {
if (!check[i]) {
prime[tot++] = i;
h[i] = (-poww(i, 4) + mod) % mod;
}
for (int j = 0; j < tot; ++j) {
if (i * prime[j] > maxn) break;
check[i * prime[j]] = true;
if (i % prime[j] == 0) {
h[i * prime[j]] = 0;
break;
}
h[i * prime[j]] = (-h[i] * poww(prime[j], 4) % mod + mod) % mod;
}
}
for (int i = 1; i <= maxn; ++i) {
pre[i] = (pre[i - 1] + h[i]) % mod;
sum5[i] = (sum5[i - 1] + poww(i, 5) % mod) % mod;
sum2[i] = (sum2[i - 1] + poww(i, 2) % mod) % mod;
}
}
LL calc(int a, int b) {
int lim = min(a, b), le, ri;
LL ret = 0;
for (int i = 1; i <= lim; i = ri + 1) {
le = i, ri = min(a / (a / i), b / (b / i));
ret = (ret + (pre[ri] - pre[le - 1] + mod) * sum2[a / i] % mod * sum2[b / i] % mod + mod) % mod;
}
return ret;
}
void work() {
int n, m;
scanf("%d%d", &n, &m);
int lim = min(n, m), le, ri;
LL ans = 0;
for (int i = 1; i <= lim; i = ri + 1) {
le = i, ri = min(n / (n / i), m / (m / i));
ans = (ans + (sum5[ri] - sum5[le - 1] + mod) % mod * calc(n / i, m / i) % mod + mod) % mod;
}
printf("Case #%d: %lld\n", ++kas, ans);
}
int main() {
init();
int T;
scanf("%d", &T);
while (T--) work();
return 0;
}
升級版(96ms)
推導
對上面的式子繼續推導,令
令
預處理
Code
#include <cstdio>
#include <iostream>
#define maxn 1000010
#define maxm maxn + 10
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7;
bool check[maxm];
int prime[maxm], kas;
LL h[maxm], pre[maxm], sum5[maxm], sum2[maxm];
LL poww(LL a, LL b) {
LL ret = 1;
while (b) {
if (b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
void init() {
h[1] = 1; int tot = 0;
for (int i = 2; i <= maxn; ++i) {
if (!check[i]) {
prime[tot++] = i;
h[i] = (poww(i, 5) - poww(i, 4) + mod) % mod;
}
for (int j = 0; j < tot; ++j) {
if (i * prime[j] > maxn) break;
check[i * prime[j]] = true;
if (i % prime[j] == 0) {
h[i * prime[j]] = h[i] * poww(prime[j], 5) % mod;
break;
}
h[i * prime[j]] = h[i] * h[prime[j]] % mod;
}
}
for (int i = 1; i <= maxn; ++i) {
pre[i] = (pre[i - 1] + h[i]) % mod;
sum2[i] = (sum2[i - 1] + poww(i, 2) % mod) % mod;
}
}
void work() {
int n, m;
scanf("%d%d", &n, &m);
int lim = min(n, m), le, ri;
LL ans = 0;
for (int i = 1; i <= lim; i = ri + 1) {
le = i, ri = min(n / (n / i), m / (m / i));
ans = (ans + (pre[ri] - pre[le - 1] + mod) % mod * sum2[n / i] % mod * sum2[m / i] % mod) % mod;
}
printf("Case #%d: %lld\n", ++kas, ans);
}
int main() {
init();
int T;
scanf("%d", &T);
while (T--) work();
return 0;
}