zoukankan      html  css  js  c++  java
  • [51Nod 1237] 最大公约数之和 (杜教筛+莫比乌斯反演)

    题目描述

    i=1nj=1n(i,j) mod (1e9+7)n<=1010sum_{i=1}^nsum_{j=1}^n(i,j)~mod~(1e9+7)\n<=10^{10}

    题目分析

    乍一看十分像裸莫比乌斯反演,然而nn的范围让人望而却步
    于是先变化一下式子
    Ans=i=1nj=1n(i,j)Ans=sum_{i=1}^nsum_{j=1}^n(i,j)
    枚举T=(i,j)T=(i,j)
    =T=1ni=1nTj=1nT[(i,j)==1]=T=1ni=1nTj=1nTdi,djμ(d)=T=1nTd=1nTμ(d)nTd2=sum_{T=1}^nsum_{i=1}^{lfloorfrac nT floor}sum_{j=1}^{lfloorfrac nT floor}[(i,j)==1]\=sum_{T=1}^nsum_{i=1}^{lfloorfrac nT floor}sum_{j=1}^{lfloorfrac nT floor}sum_{d|i,d|j}mu(d)\=sum_{T=1}^nTsum_{d=1}^{lfloorfrac nT floor}mu(d){lfloorfrac n{Td} floor}^2
    令k=Td
    =k=1nnk2φ(k)=sum_{k=1}^n{lfloorfrac n{k} floor}^2varphi(k)
    则此时可以整除分块优化,每次算出nk{lfloorfrac n{k} floor}相等的上下界i,ji,j后用莫比乌斯反演计算(Sφ(j)Sφ(i1))(Svarphi(j)-Svarphi(i-1))
    由于计算φvarphi的前缀和时记忆化处理过,所以在杜教筛外面再套了一个整除分块优化不会影响时间复杂度,复杂度仍是Θ(n23)Theta(n^{frac23})

    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;
    map<LL, LL> S; LL s[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) s[i] = (s[i-1] + phi[i]) % mod;
    }
    
    inline LL sum(LL n)
    {
    	if(n < MAXN) return s[n];
    	if(S.count(n)) return S[n];
    	LL ret = (n%mod) * ((n+1)%mod) % mod * inv2 % mod;
    	for(LL i = 2, j; i <= n; i=j+1)
    	{
    		j = n/(n/i);
    		ret = (ret - sum(n/i) * ((j-i+1)%mod) % mod) % mod;
    	}
    	return S[n] = ret;
    }
    
    inline LL solve(LL n)
    {
    	LL ret = 0;
    	for(LL i = 1, j; i <= n; i=j+1)
    	{
    		j = n/(n/i);
    		ret = (ret + ((n/i)%mod) * ((n/i)%mod) % mod * ((sum(j)-sum(i-1))%mod) % mod) % mod;
    	}
    	return ret;
    }
    int main ()
    {
    	init(); LL n;
    	scanf("%lld", &n);
    	printf("%lld
    ", (solve(n)+mod)%mod);
    }
    
  • 相关阅读:
    统计字符串中每个字母出现的次数
    三次握手和四次挥手
    select后面不能包含group by 后面没有的列
    常用adb命令
    replace和replaceAll的区别
    java统计一个字符串中某个字串出现的次数
    大厂如何解决分布式事务
    ADB 用法大全
    PBN飞越转弯Flyover衔接TF、CF航段保护区组图
    PBN旁切转弯保护区组图
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039461.html
Copyright © 2011-2022 走看看