【bzoj3309】DZY Loves Math 莫比乌斯反演+线性筛
题目描述
对于正整数x,定义f(x)为x所含质因子的最大幂指数。例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0。
给定正整数n,m,求$\sum\limits_{i=1}^n\sum\limits_{j=1}^mf(\gcd(i,j))$
输入
第一行一个数T,表示询问数。
接下来T行,每行两个数n,m,表示一个询问。
输出
对于每一个询问,输出一行一个非负整数作为回答。
样例输入
4
7558588 9653114
6514903 4451211
7425644 1189442
6335198 4957
样例输出
35793453939901
14225956593420
4332838845846
15400094813
题解
莫比乌斯反演+线性筛
(为了方便,以下公式默认$n\le m$)
首先有公式:
$\ \ \ \ \sum\limits_{i=1}^n\sum\limits_{j=1}^mf(\gcd(i,j))\\=\sum\limits_{d=1}^nf(d)\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=d]\\=\sum\limits_{d=1}^nf(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}[\gcd(i,j)=1]\\=\sum\limits_{d=1}^nf(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}\sum\limits_{j=1}^{\lfloor\frac md\rfloor}\sum\limits_{p|\gcd(i,j)}\mu(p)\\=\sum\limits_{d=1}^nf(d)\sum\limits_{p=1}^{\lfloor\frac nd\rfloor}\mu(p)\lfloor\frac n{dp}\rfloor\lfloor\frac m{dp}\rfloor$
然后令$D=dp$,可以得到:
$\ \ \ \ \sum\limits_{d=1}^nf(d)\sum\limits_{p=1}^{\lfloor\frac nd\rfloor}\mu(p)\lfloor\frac n{dp}\rfloor\lfloor\frac m{dp}\rfloor\\=\sum\limits_{D=1}^n\lfloor\frac n{D}\rfloor\lfloor\frac m{D}\rfloor\sum\limits_{p|D}\mu(p)f(\frac Dp)$
这个形式看起来非常妙,然而$n$和$m$有$10^7$之大,使用枚举倍数的调和级数时间复杂度预处理的方法的话肯定会GG(亲测就算加上$\mu\neq 0$的优化也是过不去的)。
然而经过打表可以发现,对于每个$D$,$g(D)=\sum\limits_{p|D}\mu(p)f(\frac Dp)$都是$0$、$-1$或$1$中的一个值。
至此可以猜测想出结论:$g(D)$的值只与其质因子的构成有关。
事实上,具体的结论是:当$D\neq 1$时,令$D=p_1^{a_1}p_2^{a_2}...p_k^{a_k}$,则当且仅当$a_1=a_2=...=a_k$时,$g(D)=(-1)^{k+1}$;否则$g(D)=0$。
证明:
观察$g(D)$的形式:只有当$p$中不含有平方因子时,$\mu(p)$才不为0。所以相当于:$D$的所有质因子中,每个质因子有选和不选两种选择,选了的话它的贡献就会减1。
先证明$a_1$、$a_2$、...、$a_k$不全相等时,$g(D)=0$:如果所有质因子的次数不全相等,那么考虑次数最小的那个质因子,它无论是否选择都不会影响$f(\frac Dp)$的值,只是差在$\mu(p)$的正负。这样选它和不选它,产生的贡献正好正负抵消,因此$g(D)$一定是0。
再考虑$a_1=a_2=...=a_k$的情况:当且仅当每个质因子都选择时,$f(\frac Dp)$等于$a-1$,否则等于$a$。那么如果把$a-1$的那次贡献看作$a$,那么答案应该是0。所以只需要考虑把$a-1$看成$a$的变化。显然当$k$为奇数时$\mu(p)$为负,$a-1$的贡献为负,因此答案为$1$;否则$a-1$的贡献为正,答案为$-1$。
所以就可以根据每个数的质因子构成来线性筛出每个数的$g$值。具体做法:如果把每个数写成$v·p^a$的形式,那么维护每个数的:$v$的每个质因子的次数(不全相同则为-1)、$a$的值以及质因子种类数$k$。根据这些很容易计算出当前数的$g$。注意需要考虑$v=1$的情况。
然后就可以直接求前缀和,对于每组询问枚举商即可。
时间复杂度$O(n+T\sqrt n)$
#include <cstdio> #include <algorithm> #define N 10000010 #define k 10000000 using namespace std; int v[N] , c[N] , cnt[N] , prime[N] , tot , sum[N]; bool np[N]; int main() { int i , j , T , n , m , last; long long ans; for(i = 2 ; i <= k ; i ++ ) { if(!np[i]) prime[++tot] = i , c[i] = cnt[i] = 1; for(j = 1 ; j <= tot && i * prime[j] <= k ; j ++ ) { np[i * prime[j]] = 1; if(i % prime[j] == 0) { v[i * prime[j]] = v[i] , c[i * prime[j]] = c[i] + 1 , cnt[i * prime[j]] = cnt[i]; break; } else { if(!v[i] || v[i] == c[i]) v[i * prime[j]] = c[i]; else v[i * prime[j]] = -1; c[i * prime[j]] = 1 , cnt[i * prime[j]] = cnt[i] + 1; } } if(v[i] && v[i] != c[i]) sum[i] = sum[i - 1]; else if(cnt[i] & 1) sum[i] = sum[i - 1] + 1; else sum[i] = sum[i - 1] - 1; } scanf("%d" , &T); while(T -- ) { scanf("%d%d" , &n , &m); ans = 0; for(i = 1 ; i <= n && i <= m ; i = last + 1) last = min(n / (n / i) , m / (m / i)) , ans += (long long)(n / i) * (m / i) * (sum[last] - sum[i - 1]); printf("%lld\n" , ans); } return 0; }