zoukankan      html  css  js  c++  java
  • [武汉加油] 2020.2.15 考试T1

    题目描述

    给定一个(n),求(displaystyle sum_{i=1}^nsum_{p|i}d(p)),d(n)表示n的约数个数。

    (n le 10^{11})

    方法一:

    原式等价于(displaystyle sum_{i=1}^{n}lfloor frac{n}{i} floor d(i))

    线性筛即可

    代码就不放了。

    方法二:

    对于上面的式子(displaystyle sum_{i=1}^{n}lfloor frac{n}{i} floor d(i))我们可以数论分块,问题就变成了如何快速的求约数个数的前缀和

    能1s跑(10^{10})不成问题。但考试的时候没有这部分分。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define LL long long 
    using namespace std;
    LL n,ans,tot,M;
    const int N=8000010;
    int zhi[N],num[N],mu[N];
    long long d[N];
    bool vis[N];
    map<LL,LL>smu,sd;
    void HCYY()
    {
    	mu[1]=d[1]=1;
    	for(register int i=2;i<=M;++i)
    	{
    		if(!vis[i])zhi[++tot]=i,mu[i]=-1,num[i]=1,d[i]=2;
    		for(int j=1;j<=tot&&i*zhi[j]<=M;++j)
    		{
    			vis[i*zhi[j]]=1;
    			if(!(i%zhi[j]))
    			{
    				num[i*zhi[j]]=num[i]+1;
    				d[i*zhi[j]]=d[i]/(num[i]+1)*(num[i]+2);
    				break;
    			}
    			num[i*zhi[j]]=1;
    			d[i*zhi[j]]=d[i]*d[zhi[j]];
    			mu[i*zhi[j]]=-mu[i];
    		}
    	}
    	for(int i=2;i<=M;++i)mu[i]+=mu[i-1],d[i]+=d[i-1];
    }
    LL suan1(LL n)//mu
    {
    	if(n<=M)return mu[n];
    	if(smu.count(n))return smu[n];
    	LL ans=1;
    	for(LL l=2,r;l<=n;l=r+1)r=n/(n/l),ans-=(r-l+1)*suan1(n/l);
    	return smu[n]=ans;
    }
    LL suan2(LL n)//d
    {
    	if(n<=M)return d[n];
    	if(sd.count(n))return sd[n];
    	LL res=n;
    	for(LL l=2,r,las=1,now;l<=n;l=r+1)
    	{
    		r=n/(n/l);
    		now=suan1(r);
    		res-=(now-las)*suan2(n/l);
    		las=now;
    	}
    	return sd[n]=res;
    }
    void solve3()
    {
    	M=min(N-10,(int)pow(n,0.66));
    	HCYY();
    	for(LL l=1,r,las=0,now;l<=n;l=r+1)
    	{
    		r=n/(n/l);
    		now=suan2(r);
    		ans+=(n/l)*(now-las);
    		las=now;
    	}
    	cout<<ans;
    }
    int main()
    {
    	cin>>n;
    	solve3();
    	return 0;
    }
    

    方法三:

    原题等价于求 (xyz le n) 的有序三元组个数.

    原题等价于求(displaystyle sum_{i=1}^{n}sum_{p|i}sum_{d|p}1),把i拆两次能得到3个数。

    假设 (x le y le z),则 (x le n ^{frac{1}{3}})(displaystyle y le sqrt frac{n}{x})

    枚举 x, y,我们可以计算出合法的 z 的数量.

    假设合法数为 ans,则 x, y, z 可以任意排列,因此 ans ∗ 6.

    但是相等情况会重复计数,我们再计算有两个数相等、三个数全相等的情况数.

    两个数相等(displaystyle sum frac{n}{i^2}),三个数全相等:(n ^{frac{1}{3}})

    容斥一下就行啦。

    代码出奇的短.

    #include<iostream>
    #include<cmath>
    #define LL long long 
    using namespace std;
    LL n,ans;
    int main()
    {
    	cin>>n;
    	LL toi=floor(pow(n,0.34));while(toi*toi*toi>n)--toi;
    	for(LL i=1;i<=toi;++i)
    		for(LL j=i,toj=sqrt(n/i);j<=toj;++j)ans+=n/(i*j)-j+1;
    	ans*=6;
    	for(LL i=1;i*i<=n;++i)ans-=(n/i/i)*3;
    	ans-=2*toi;
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    CMS前世今生
    叫练手把手教你读JVM之GC信息
    原来我还有网络天赋
    最简单的JVM内存结构图
    图解CyclicBarrier运动员接力赛
    图解定时任务线程池
    并发队列:ArrayBlockingQueue实际运用场景和原理
    ReentrantReadWriteLock读写锁简单原理案例证明
    JavaScript中的Function类型浅析
    JS数组整理
  • 原文地址:https://www.cnblogs.com/wljss/p/12312340.html
Copyright © 2011-2022 走看看