[BZOJ2005][NOI2010]能量采集(莫比乌斯反演)

来源:互联网 发布:云计算iaas paas saas 编辑:程序博客网 时间:2024/06/02 05:39

题意:给定n,m,求ni=1mi=1(2gcd(i,j)1)
容易推出,原式可化为2ni=1mi=1gcd(i,j)nm
而关键就是求ni=1mi=1gcd(i,j)的值。考虑先枚举gcd(i,j)的值,就可以得到:
原式=min(n,m)d=1ni=1mi=1[gcd(i,j)=d]
=min(n,m)d=1ndi=1mdi=1[gcd(i,j)=1]
而求xi=1yi=1[gcd(i,j)=1]是莫比乌斯反演中最经典的模型。这个式子等于min(x,y)d=1μ(d)xdyd。可以在根号的时间内求出,具体做法可查阅其他资料。
补充一点:如果有多组询问,那么:
ni=1mi=1gcd(i,j)=min(n,m)u=1numud|u(μ(ud)d)
d|u(μ(ud)d)的值和前缀和可以预处理。证明由读者去思考。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}typedef long long ll;const int MaxN = 1e5, N = MaxN + 5;int n, m, tot, pri[N], miu[N], sum[N];bool mark[N];void sieve() {    int i, j; mark[0] = mark[1] = 1; miu[1] = 1;    for (i = 2; i <= MaxN; i++) {        if (!mark[i]) pri[++tot] = i, miu[i] = -1;        for (j = 1; j <= tot; j++) {            if (1ll * i * pri[j] > MaxN) break;            mark[i * pri[j]] = 1;            if (i % pri[j] == 0) break;            else miu[i * pri[j]] = -miu[i];        }    }    for (i = 1; i <= MaxN; i++) sum[i] = sum[i - 1] + miu[i];}ll solve(int n, int m) {    int i; ll ans = 0;    for (i = 1; i <= min(n, m);) {        int nxt = min(n / (n / i), m / (m / i));        ans += 1ll * (sum[nxt] - sum[i - 1]) * (n / i) * (m / i);        i = nxt + 1;    }    return ans;}int main() {    int i; n = read(); m = read(); ll ans = 0; sieve();    for (i = 1; i <= min(n, m); i++)        ans += solve(n / i, m / i) * i;    cout << (ans - 1ll * n * m) * 2 + 1ll * n * m << endl;    return 0;}