• [51Nod 1238] 最小公倍数之和 (恶心杜教筛)


    题目描述

    i=1Nj=1Nlcm(i,j)sum_{i=1}^Nsum_{j=1}^Nlcm(i,j)
    2<=N<=10102<=N<=10^{10}

    题目分析

    这道题题面跟[bzoj 2693] jzptab & [bzoj 2154] Crash的数字表格 一样,然而数据范围加强到了101010^{10},莫比乌斯反演不行了了,所以我们看看怎样玄学杜教筛
    Ans=i=1nj=1nlcm(i,j)=2i=1nj=1ilcm(i,j)n(n+1)2Let s(n)=i=1nj=1ilcm(i,j),f(n)=i=1nlcm(i,n)f(n)=i=1nin(i,n)=ni=1ni(i,n)=ndni=1n[(i,n)==d]id=ndni=1nd[(i,nd)==1]i=ndni=1d[(i,d)==1]i=ndnφ(d)d+[d==1]2large Ans=sum_{i=1}^nsum_{j=1}^nlcm(i,j)=2sum_{i=1}^nsum_{j=1}^ilcm(i,j)-frac{n(n+1)}{2}\Let~s(n)=sum_{i=1}^nsum_{j=1}^ilcm(i,j),f(n)=sum_{i=1}^nlcm(i,n)\ herefore f(n)=sum_{i=1}^nfrac{in}{(i,n)}=nsum_{i=1}^nfrac i{(i,n)}\=nsum_{d|n}sum_{i=1}^n[(i,n)==d]frac id\=nsum_{d|n}sum_{i=1}^{frac nd}[(i,frac nd)==1]i\=nsum_{d|n}sum_{i=1}^d[(i,d)==1]i\=nsum_{d|n}frac{varphi(d)d+[d==1]}2
    此处有一个常识
    i=1ni[(i,n)==1]=φ(n)n+[n==1]2sum_{i=1}^ni[(i,n)==1]=frac {varphi(n)n+[n==1]}2

    • 证明如下
      • n>1n>1时,若(i,n)=1    (ni,n)=1(i,n)=1iff(n-i,n)=1,所以与nn互质的数是成对出现,且他们的和为nn
      • 再加之n=1n=1的特殊情况,可得
        i=1ni[(i,n)==1]=φ(n)n+[n==1]2large sum_{i=1}^ni[(i,n)==1]=frac {varphi(n)n+[n==1]}2

    继续
    f(n)=n1+dnφ(d)d2s(n)=i=1nf(i)=i=1ni(1+diφ(d)d)2=n(n+1)2+i=1nidiφ(d)d2=n(n+1)2+d=1nφ(d)ddii2=n(n+1)2+d=1nφ(d)d2i=1ndi2=n(n+1)2+i=1nid=1niφ(d)d22Ans=2s(n)n(n+1)2=i=1nid=1niφ(d)d2Let h(d)=φ(d)d2,g(n)=d=1nh(d)n=dnφ(d)n3=dnφ(d)n2=dnφ(d)d2(nd)2=dnh(d)(nd)2i=1ni3=i=1ndnh(d)(id)2=d=1nh(d)di(id)2=d=1nh(d)i=1ndi2=i=1ni2d=1nih(d)=i=1ni2g(ni)g(n)=i=1ni3i=2ni2g(ni)=(n(n+1)2)2i=2ni2g(ni) large herefore f(n)=ncdotfrac {1+sum_{d|n}varphi(d)d}2\s(n)=sum_{i=1}^nf(i)=frac{sum_{i=1}^ni(1+sum_{d|i}varphi(d)d)}2\=frac{frac{n(n+1)}2+sum_{i=1}^nisum_{d|i}varphi(d)d}2\=frac{frac{n(n+1)}2+sum_{d=1}^nvarphi(d)dsum_{d|i}i}2\=frac{frac{n(n+1)}2+sum_{d=1}^nvarphi(d)d^2sum_{i=1}^{lfloorfrac nd floor}i}2\=frac{frac{n(n+1)}2+sum_{i=1}^nisum_{d=1}^{lfloorfrac ni floor}varphi(d)d^2}2\Ans=2s(n)-frac{n(n+1)}2=sum_{i=1}^nisum_{d=1}^{lfloorfrac ni floor}varphi(d)d^2\Let~h(d)=varphi(d)d^2,g(n)=sum_{d=1}^nh(d)\n=sum_{d|n}varphi(d)\n^3=sum_{d|n}varphi(d)n^2\=sum_{d|n}varphi(d)d^2(frac nd)^2\=sum_{d|n}h(d)(frac nd)^2\sum_{i=1}^ni^3=sum_{i=1}^nsum_{d|n}h(d)(frac id)^2\=sum_{d=1}^nh(d)sum_{d|i}(frac id)^2\=sum_{d=1}^nh(d)sum_{i=1}^{lfloorfrac nd floor}i^2\=sum_{i=1}^ni^2sum_{d=1}^{lfloorfrac ni floor}h(d)\=sum_{i=1}^ni^2g(lfloorfrac ni floor)\g(n)=sum_{i=1}^ni^3-sum_{i=2}^ni^2g(lfloorfrac ni floor)\=(frac{n(n+1)}2)^2-sum_{i=2}^ni^2g(lfloorfrac ni floor)然后就是杜教筛的形式了,上杜教筛即可.先预处理出小范围的gg然后较大的就用杜教筛计算

    Ans=i=1nig(ni)large Ans=sum_{i=1}^nicdot g(lfloorfrac ni floor)

    因为gg函数求解时是用的记忆化,所以在外面套上一层分块优化不会影响gg函数的时间复杂度,所以复杂度为Θ(n23)Theta(n^{frac 23})

    AC code
    #include <cstdio>
    #include <map>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int mod = 1e9+7;
    const int MAXN = 5e6+1;
    const int inv2 = 500000004;
    const int inv3 = 333333336;
    map<LL, LL> G; LL g[MAXN];
    int Prime[MAXN], Cnt, phi[MAXN];
    bool IsnotPrime[MAXN];
    
    void init()
    {
    	phi[1] = 1;
    	for(int i = 2; i < MAXN; ++i)
    	{
    		if(!IsnotPrime[i]) Prime[++Cnt] = i, phi[i] = i-1;
    		for(int j = 1; j <= Cnt && i * Prime[j] < MAXN; ++j)
    		{
    			IsnotPrime[i * Prime[j]] = 1;
    			if(i % Prime[j] == 0)
    			{
    				phi[i * Prime[j]] = phi[i] * Prime[j];
    				break;
    			}
    			phi[i * Prime[j]] = phi[i] * phi[Prime[j]];
    		}
    	}
    	for(int i = 1; i < MAXN; ++i) g[i] = (g[i-1] + 1ll * phi[i] * i % mod * i % mod) % mod;
    }
    
    inline LL sum2(LL i) { return (i%mod) * ((i+1)%mod) % mod * ((2*i+1)%mod) % mod * inv2 % mod * inv3 % mod; }
    
    inline LL calc(LL n)
    {
    	if(n < MAXN) return g[n];
    	if(G.count(n)) return G[n];
    	LL ret = (n%mod) * ((n+1)%mod) % mod * inv2 % mod;
    	ret = ret * ret % mod;
    	for(LL i = 2, j; i <= n; i=j+1)
    	{
    		j = n/(n/i);
    		ret = (ret - (sum2(j)-sum2(i-1)) % mod * calc(n/i) % mod) % mod;
    	}
    	return G[n] = ret;
    }
    
    inline LL sum(LL i, LL j) { return ((i+j)%mod) * ((j-i+1)%mod) % mod * inv2 % mod; }
    
    inline LL solve(LL n)
    {
    	LL ret = 0;
    	for(LL i = 1, j; i <= n; i=j+1)
    	{
    		j = n/(n/i);
    		ret = (ret + sum(i, j) * calc(n/i) % mod) % mod;
    	}
    	return ret;
    }
    int main ()
    {
    	init(); LL n;
    	scanf("%lld", &n);
    	printf("%lld
    ", (solve(n)+mod)%mod);
    }
    
  • 相关阅读:
    mysql索引
    mysql事务
    MySQL注入问题
    【CUDA并行编程之四】矩阵相乘
    【CUDA并行编程之三】Cuda矢量求和运算
    【Cuda并行编程之二】Cuda Memory Hierarchy_Cuda内存层次结构
    【Cuda并行编程之一】二分查找的探究以及Cuda的简单实现&&相关面试题介绍
    CUDA编程接口:共享存储器实现矩阵相乘
    CUDA学习
    CUDA从入门到精通
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039460.html
Copyright © 2020-2023  润新知