• 【2020杭电多校round6】HDU6834 Yukikaze and Smooth numbers


    题目大意

    题目链接

    给定(n,k)。我们认为一个正整数是合法的,当且仅当它所有质因数都小于等于(k)。求有多少小于等于(n)的合法的正整数。

    数据范围:(T)组测试数据,(1leq Tleq 50)(1leq n,kleq 10^9)

    本题题解

    (kgeq n)时,显然所有(leq n)的正整数都是合法的,答案就是(n),我们可以特判。以下只考虑(k<n)的情况。


    (k>sqrt{n})时,任何数只要有一个(>k)的质因子,都是不合法的;而此时一个小于等于(n)的正整数,至多只有(1)(>k)的质因子。换句话说,此时一个数(x) ((1leq xleq n))不合法当且仅当存在某个质数(p>k),满足(x)(p)的倍数;并且一个(x)最多只可能是一个(p)的倍数。所以,不合法的数的总数就是(sum_{i=k+1}^{n}[i ext{是质数}]lfloorfrac{n}{i} floor),答案就是(n)减去这个数量。

    (k+1)开始求和比较麻烦。先考虑求(s(n)=sum_{i=1}^{n}[i ext{是质数}]lfloorfrac{n}{i} floor)

    这个(s)函数,无论是它本身,还是它在质数处的取值,都不是积性函数,很难用数论上常用的筛法求。因此我们需要进一步转化。仔细观察这个式子,发现(lfloorfrac{n}{i} floor)是个好东西,因为对于任意一个(n)(lfloorfrac{n}{i} floor)只有(O(sqrt{n}))种不同的取值。我们可以用数论分块枚举这些取值,并且能知道这个取值对应的区间(l,r),那么问题就转化为求([l,r])区间里有多少质数。

    我们还发现一个好消息,这里的([l,r])不是随意的,而是总是存在一个(i)使得(r=lfloorfrac{n}{i} floor),而(l)就是上一个(r)(1)。这正是( ext{min25})筛法的前半部分:对于所有(rin{lfloorfrac{n}{i} floor | 1leq ileq n}),求出(sum_{i=1}^{r}[i ext{是质数}]f(i))。这里(f(i))被要求在质数处的取值是一个完全积性函数,在本题中,因为是求质数个数,我们可以认为(f(i)=1),显然这个函数是完全积性的。

    ( ext{min25})筛法的过程不详细介绍了,不会的可以去看我写的良心入门教程

    还有最后一个小问题:我们真正要算的是从(k+1)开始的,而不是从(1)开始的,这就意味着我们的第一段(l),它不一定是(lfloorfrac{n}{i} floor+1)的形式。不过这也很好办,我们用同样的方法,筛出(leq k)的质数个数,作为初始值即可。

    时间复杂度(O(sqrt{n}+frac{n^{frac{3}{4}}}{log n}))


    以上只是(k>sqrt{n})的情况。当(kleq sqrt{n})时,没有什么特别清晰的好方法。不过我们可以先把(leq k)的质数筛出来,然后用这些质数爆搜出合法的数的数量。

    朴素的( ext{dfs})还是太慢了,需要加一些剪枝。

    • 比如说,如果【当前乘积】乘以【当前考虑的质数】,已经(>n)了,则后面更大的质数一定不会选上,所以可以直接累加答案并返回(不需要往后走到最后一个质数再返回)。
    • 再比如说,如果【当前乘积】乘以【当前考虑的质数的平方】,已经(>n)了,则后面的质数里,至多只会再选(1)个。我们二分出可以选的最大质数,然后把方案数累加到答案里,并直接返回。

    加上这两个剪枝后,爆搜就跑的非常快了,足以通过(kleq sqrt{n})的情况。


    参考代码:

    //problem:HDU6834(1008)
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
    template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
    
    const int MAXN=31623;//sqrt(MAXN)
    int n,K;
    int p[MAXN+5],cnt_p;
    bool v[MAXN+5];
    void sieve(int lim){
    	for(int i=2;i<=lim;++i){
    		if(!v[i]){
    			p[++cnt_p]=i;
    		}
    		for(int j=1;j<=cnt_p && i*p[j]<=lim;++j){
    			v[i*p[j]]=1;
    			if(i%p[j]==0){
    				break;
    			}
    		}
    	}
    }
    int ans,lim;
    void dfs(int idx,int prod){
    	if(idx==lim){
    		ans++;
    		return;
    	}
    	if((ll)prod*p[idx]>n){
    		ans++;
    		return;
    	}
    	if((ll)prod*p[idx]*p[idx]>n){
    		int l=idx,r=lim;
    		while(r-l>1){
    			int mid=(l+r)/2;
    			if((ll)prod*p[mid]<=n)l=mid;
    			else r=mid;
    		}
    		ans+=l-idx+2;
    		return;
    	}
    	for(ll x=1;;x*=p[idx]){
    		if(x*prod > n)break;
    		dfs(idx+1,x*prod);
    	}
    }
    
    struct Min25{
    	int n,sqrt_n;
    	int val[MAXN*2+5],id1[MAXN+5],id2[MAXN+5],tot;
    	int g[MAXN*2+5];
    	inline int get_id(int w){
    		if(w<=sqrt_n) return id1[w];
    		else return id2[n/w];
    	}
    	void build(int _n){
    		n=_n;
    		tot=0;
    		sqrt_n=sqrt(n);
    		for(int i=1,j;i<=n;i=j+1){
    			j=n/(n/i);
    			int w=n/i;
    			val[++tot]=w;
    			if(w<=sqrt_n) id1[w]=tot;
    			else id2[n/w]=tot;
    			
    			g[tot]=w-1;
    		}
    		for(int j=1;j<=cnt_p;++j){
    			for(int i=1;i<=tot && (ll)p[j]*p[j]<=val[i];++i){
    				int k=get_id(val[i]/p[j]);
    				g[i]=g[i]-(g[k]-(j-1));
    			}
    		}
    	}
    	Min25(){}
    }SN,SK;
    
    
    void solve_case(){
    	cin>>n>>K;
    	if(K>=n){
    		cout<<n<<endl;
    		return;
    	}
    	if(K<=MAXN){
    		ans=0;
    		lim=cnt_p+1;
    		for(int i=1;i<=cnt_p;++i){
    			if(p[i]>K){
    				lim=i;
    				break;
    			}
    		}
    		dfs(1,1);
    		cout<<ans<<endl;
    	}
    	else{
    		SN.build(n);
    		SK.build(K);
    		ans=0;
    		int lst=SK.g[1];
    		for(int i=K+1,j;i<=n;i=j+1){
    			j=n/(n/i);
    			int k=SN.get_id(j);
    			assert(SN.val[k]==j);
    			ans+=(SN.g[k]-lst)*(n/i);
    			lst=SN.g[k];
    		}
    		cout<<n-ans<<endl;
    	}
    }
    int main() {
    	sieve(MAXN);
    	int T;cin>>T;while(T--){
    		solve_case();
    	}
    	return 0;
    }
    
  • 相关阅读:
    SQL_50题
    Java多线程之冰山一角
    概览
    Selector
    集群配置
    redis config
    分布式、集群
    redis相关技术总结
    redis scan扫描
    redis 单线程 多路io复用
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13449183.html
Copyright © 2020-2023  润新知