zoukankan      html  css  js  c++  java
  • 【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,求$sumlimits_{i=1}^nsumlimits_{j=1}^mf(gcd(i,j))$

    输入

    第一行一个数T,表示询问数。
    接下来T行,每行两个数n,m,表示一个询问。

    输出

    对于每一个询问,输出一行一个非负整数作为回答。

    样例输入

    4
    7558588 9653114
    6514903 4451211
    7425644 1189442
    6335198 4957

    样例输出

    35793453939901
    14225956593420
    4332838845846
    15400094813


    题解

    莫比乌斯反演+线性筛

    (为了方便,以下公式默认$nle m$)

    首先有公式:

    $ sumlimits_{i=1}^nsumlimits_{j=1}^mf(gcd(i,j))\=sumlimits_{d=1}^nf(d)sumlimits_{i=1}^nsumlimits_{j=1}^m[gcd(i,j)=d]\=sumlimits_{d=1}^nf(d)sumlimits_{i=1}^{lfloorfrac nd floor}sumlimits_{j=1}^{lfloorfrac md floor}[gcd(i,j)=1]\=sumlimits_{d=1}^nf(d)sumlimits_{i=1}^{lfloorfrac nd floor}sumlimits_{j=1}^{lfloorfrac md floor}sumlimits_{p|gcd(i,j)}mu(p)\=sumlimits_{d=1}^nf(d)sumlimits_{p=1}^{lfloorfrac nd floor}mu(p)lfloorfrac n{dp} floorlfloorfrac m{dp} floor$

    然后令$D=dp$,可以得到:

    $ sumlimits_{d=1}^nf(d)sumlimits_{p=1}^{lfloorfrac nd floor}mu(p)lfloorfrac n{dp} floorlfloorfrac m{dp} floor\=sumlimits_{D=1}^nlfloorfrac n{D} floorlfloorfrac m{D} floorsumlimits_{p|D}mu(p)f(frac Dp)$

    这个形式看起来非常妙,然而$n$和$m$有$10^7$之大,使用枚举倍数的调和级数时间复杂度预处理的方法的话肯定会GG(亲测就算加上$mu eq 0$的优化也是过不去的)。

    然而经过打表可以发现,对于每个$D$,$g(D)=sumlimits_{p|D}mu(p)f(frac Dp)$都是$0$、$-1$或$1$中的一个值。

    至此可以猜测想出结论:$g(D)$的值只与其质因子的构成有关。

    事实上,具体的结论是:当$D eq 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+Tsqrt 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
    " , ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    07组合,模版
    06享元、责任链
    05观察,命令
    04代理,迭代器
    03单例,策略
    02工厂,创建者
    01基础
    css随记02布局
    css随记01编辑技巧,背景与边框
    nodejs随记03
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7514610.html
Copyright © 2011-2022 走看看