BZOJ 3309 DZY Loves Math

来源:互联网 发布:淘宝网百雀羚旗舰店 编辑:程序博客网 时间:2024/05/17 18:25

题目链接

题意

i=1aj=1bf(gcd(i,j))

其中
f(x)={α1,x=p1α1p2α2+...+pnαn,α1>α2,...,αn0,x=1

推导

法一:艾弗森约定

gcd(i,j)=k, 则有 gcd(ik,jk)=1,
故原式可化为

k=1min(a,b)i=1akj=1bkf(k)[gcd(i,j)=1]

用莫比乌斯函数将艾弗森约定展开得
k=1min(a,b)f(k)i=1akj=1bkd|i,d|jμ(d)

k=1min(a,b)f(k)d=1min(ak,bk)μ(d)i=1akdj=1bkd

k=1min(a,b)f(k)d=1min(ak,bk)μ(d)akdbkd

T=kd, 得
T=1min(a,b)aTbTd|Tf(Td)μ(d)

显然,接下来的路子就是求前缀和,然后分块计算,然而 关键问题 就是怎么求这个
d|Tf(Td)μ(d)
呢?

参考PoPoQQQ可知,对于

g(T)=d|Tf(Td)μ(d),T=p1α1p2α2...pnαn

g(T)={0,αiαj,(1)(n+1),otherwise

具体证明见上附链接,要点为从 k 个元素中选取奇数个元素的种数 = 选取偶数个元素的种数,因为 (11)k=0C0k+C2k+...=C1k+C3k+...

法二:莫比乌斯反演(待补)

Code

#include <bits/stdc++.h>#define maxn 10000000#define maxm maxn + 10using namespace std;typedef long long LL;bool check[maxm];int prime[maxm], cnt[maxm], val[maxm], g[maxm], sum[maxm];void init() {    int tot = 0; g[1] = 0;    for (int i = 2; i <= maxn; ++i) {        if (!check[i]) {            prime[tot++] = i;            cnt[i] = 1, val[i] = i, g[i] = 1;        }        for (int j = 0; j < tot; ++j) {            if (i * prime[j] > maxn) break;            check[i * prime[j]] = true;            if (i % prime[j] == 0) {                cnt[i * prime[j]] = cnt[i] + 1, val[i * prime[j]] = val[i] * prime[j];                int temp = (i * prime[j]) / val[i * prime[j]];                if (temp == 1) g[i * prime[j]] = 1;                else g[i * prime[j]] = (cnt[temp] == cnt[i * prime[j]] ? -g[temp] : 0);                break;            }            cnt[i * prime[j]] = 1, val[i * prime[j]] = prime[j];            int temp = (i * prime[j]) / val[i * prime[j]];            if (temp == 1) g[i * prime[j]] = 1;            else g[i * prime[j]] = (cnt[temp] == cnt[i * prime[j]] ? -g[temp] : 0);        }    }    for (int i = 1; i <= maxn; ++i) sum[i] = sum[i - 1] + g[i];}void work() {    LL a, b;    scanf("%lld%lld", &a, &b);    int le, ri, lim = min(a, b);    LL ans = 0;    for (int i = 1; i <= lim; i = ri + 1) {        le = i, ri = min(a / (a / i), b / (b / i));        ans += (a / i) * (b / i) * (sum[ri] - sum[le - 1]);    }    printf("%lld\n", ans);}int main() {    init();    int T;    scanf("%d", &T);    while (T--) work();    return 0;}
原创粉丝点击