zoukankan      html  css  js  c++  java
  • 杜教筛:Bzoj3944: sum

    题意

    (sum_{i=1}^{n}varphi(i)和sum_{i=1}^{n}mu(i))
    (n <= 2^{31}-1)

    不会做啊。。。
    只会线性筛,显然不能线性筛
    这个时候就需要杜教筛

    怎么筛

    先看一下狄利克雷卷积
    假设我们要求(F(i)=sum_{i=1}^{n}f(n))(n(10^{11}左右))比较大不能线性筛时考虑杜教筛
    套路的推导:
    先随意找一个函数(g(i))(f(i))求狄利克雷卷积:
    $$(g * f)(n) = sum_{d|n} g(d)f(frac{n}{d})$$
    它的前缀和就是
    $$sum_{i=1}^{n}sum_{d|i} g(d)f(frac{i}{d})$$
    现在要想办法向(F(n))上靠:
    $$原式=sum_{d=1}^{n}sum_{d|i}g(d)f(frac{i}{d})=sum_{d=1}^{n}sum_{i=1}^{lfloorfrac{n}{d} floor}g(d)f(i)$$
    $$=sum_{d=1}^{n}g(d)sum_{i=1}^{lfloorfrac{n}{d} floor}f(i)=sum_{d=1}^{n}g(d)F(lfloorfrac{n}{d} floor)$$
    所以

    [g(1)F(n)=(g * f)(n)-sum_{d=2}^{n}g(d)F(lfloorfrac{n}{d} floor) ]

    (g(1)=1时最好)
    如果能够快速的对(g)(g * f)求和,那么就能在(O(n^{frac{3}{4}})的时间内计算出F(n))复杂度证明略不会
    如果(f)为积性函数,还可以线性筛出(F)的前若干项((n^{frac{2}{3}})最优)降低复杂度

    Sol

    再看这道题

    先看(varphi):

    (phi(i)=sum_{i=1}^{n}varphi(i))
    按上面的来
    $$g(1)phi(n)=sum_{i=1}^{n}(gvarphi)(i)-sum_{i=2}^{n}g(d)phi(lfloorfrac{n}{d} floor)$$
    有个定理(sum_{d|n}varphi(d)=n)
    (g(i)=1)所以
    $$sum_{i=1}^{n}(g
    varphi)(i)=sum_{i=1}^{n}sum_{d|i}varphi(frac{i}{d})=frac{n*(n + 1)}{2}$$
    线性筛一部分,带进去递归处理即可

    再看(mu):

    (U(n)=sum_{i=1}^{n}mu(i))
    一样的套路得到
    $$g(1)U(n)=sum_{i=1}^{n}sum_{d|i}g(d)mu(frac{i}{d})-sum_{i=2}^{n}g(d)U(lfloorfrac{n}{d} floor)$$
    注意到(sum_{d|n}mu(d)=[n=1]),直接令(g(i)=1)
    $$U(n)=1-sum_{i=2}^{n}g(d)U(lfloorfrac{n}{d} floor)$$
    一样的递归处理即可

    一定要记忆化(我用的map)

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int _(5e6 + 1);
    
    IL ll Read(){
        RG ll x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int prime[_], num;
    ll phi[_], mu[_];
    map <int, ll> Phi, Mu;
    bool isprime[_];
    
    IL void Prepare(){
    	isprime[1] = 1; phi[1] = mu[1] = 1;
    	for(RG int i = 2; i < _; ++i){
    		if(!isprime[i]){  prime[++num] = i; mu[i] = -1; phi[i] = i - 1;  }
    		for(RG int j = 1; j <= num && i * prime[j] < _; ++j){
    			isprime[i * prime[j]] = 1;
    			if(i % prime[j]){  mu[i * prime[j]] = -mu[i]; phi[i * prime[j]] = phi[i] * (prime[j] - 1);  }
    			else{  mu[i * prime[j]] = 0; phi[i * prime[j]] = phi[i] * prime[j]; break;  }
    		}
    	}
    	for(RG int i = 2; i < _; ++i) mu[i] += mu[i - 1], phi[i] += phi[i - 1];
    }
    
    IL ll Sumphi(RG ll n){
    	if(n < _) return phi[n];
    	if(Phi[n]) return Phi[n];
    	RG ll ans = n * (n + 1) / 2;
    	for(RG ll i = 2, j; i <= n; i = j + 1){
    		j = n / (n / i);
    		ans -= (j - i + 1) * Sumphi(n / i);
    	}
    	return Phi[n] = ans;
    }
    
    IL ll Summu(RG ll n){
    	if(n < _) return mu[n];
    	if(Mu[n]) return Mu[n];
    	RG ll ans = 1;
    	for(RG ll i = 2, j; i <= n; i = j + 1){
    		j = n / (n / i);
    		ans -= (j - i + 1) * Summu(n / i);
    	}
    	return Mu[n] = ans;
    }
    
    int main(RG int argc, RG char* argv[]){
    	Prepare();
    	for(RG int T = Read(); T; --T){
    		RG ll n = Read();
    		printf("%lld %lld
    ", Sumphi(n), Summu(n));
    	}
        return 0;
    }
    
    
  • 相关阅读:
    团队冲刺第二十三天
    团队冲刺第二十二天
    团队冲刺第二十一天
    团队冲刺第二十天
    第十四周周总结
    团队冲刺第十九天
    团队冲刺第十八天
    团队冲刺第十七天
    团队冲刺第十六天
    keeprunning的使用说明
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8297779.html
Copyright © 2011-2022 走看看