NOIP 2016模拟赛1 T1 最多因数

T1 最多因数

(MFactor.cpp/c/pas)

【问题描述】
给两个数a,b,输出两个数之间因数个数最多的数。如果有多个因数相同的数,输出最小
的那个。
【输入格式】
两个非负整数
【输出格式】
一个整数
【样例输入】
6 10
【样例输出】
6
【样例解释】
6,8,10 都有4 个因数,但6 最小。
【数据范围】
30% 0<=a<=b<=1000
60% 0<=a<=b<=100000

100% 0<=a<=b<=2000000000, 当数据大于100000 时,保证b-a=300000


非常非常有意思的一道搜索题,很多人看到这个数据都不会想到搜索,可是实际上他就是个搜索

首先我们根据排列组合相关公式可以得到以下结论:

对于一个数,如果可以将其分解质因数p[1]^a[1] * p[2]^a[2] *.... * p[k]^a[k],那么其因数个数为:(a[1]+1)*(a[2]+1)*....*(a[k]+1)

最容易想的方法就是枚举区间的每一个数并且依次分解,这样正确性是显然的,但是只能30分(常数过大也许还没有30分)

接着分析:如果p是一个质数,那么在算n的质因数和p*n的质因数的时候,n的质因数就被算了两次,这就是时间效率不高的原因之一,因此我们考虑枚举每一个质因数来构成一个新的数,这样就避免了这种问题

但是很遗憾,这种方法仍然是会超时的,主要是因为数据范围造成的,因此只能再次优化算法

假如说当前我们通过枚举质因数得到了一个新的数k,在朴素的搜索中即使[kk*cur](cur为当前枚举的质数)与题中所给区间没有交点,搜索还是会继续进行下去,这也就是另外一个效率不高的原因,那么如何判断一个数k是否符合条件呢

很容易证明,如果(L– 1) /K< R/K,则区间内存在可以被K整除的数。因为,如果区间[L, R]内存在可以被K整除的数,也即是从LR中至少有一个数能被K整除,那么区间[L– 1, R]内的数被Kr除得的商肯定不止一种,所以(L– 1) /K必然小于R/K

反过来,如果(min-1)div number=max div number,[min,max]内不存在可以被number整除的数.

题中的数据范围虽然很大,但是一个数大于sqrt(2000000000)的质因数显然最多只有一个,因此筛因数的时候只筛

到100000就差不多可以了

因此对于一个数k,如果枚举了100000以内的所有质因数都无法在[L,R]内,那么这个数一定含有一个大于100000的质因数,根据上面的公式,此时的primecnt只需乘以2即可

我们还能看到,如果当前搜索状态为(cur,k,primcnt),其中cur是指当前枚举到的质因子(按从小到大枚举),primcnt是指k中包含的约数个数。那么剩下的因子数最多为q = [logcur(R / K)],这些因子组成的约数个数(即上述求约数个数时用到的一串乘积)最大为2q。当前所能取到的(理想情况)最大约数个数就是primcnt * 2q,如果这个数仍然无法超过当前最优解,则这一分支可以剪去。

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;
const LL maxn=100005,inf=0x3f3f3f3f;
LL prime[maxn],Next[maxn],cnt,ans=inf,tot,l,r;
bool vis[maxn];
void prime_table(){//线性筛质数
	int i,j;
	for(i=2;i<=maxn;i++){
		if(!vis[i])prime[++tot]=i,Next[i]=tot;
		for(j=1;prime[j]*i<=maxn&&j<=tot;j++){
			vis[prime[j]*i]=1,Next[prime[j]*i]=j;
			if(i%prime[j]==0)break;
		}
	}
}
void search(LL cur,LL primcnt,LL num,LL L,LL R){
    if(num>=1)
        if(primcnt>=cnt&&num>=l&&num<=r){//这个地方一定记得判断num是否在区间内,否则可能比左端点小
        	if(primcnt>cnt)cnt=primcnt,ans=num;
            else ans=min(ans,num);
        }
    LL i,t,x,xx,y,temp,curn,curt;
    if(L==R&&L>num)search(cur,primcnt*2,num*L,1,1);//即num含有一个大于100000的因子
    for(i=cur;i<=tot;i++){//搜索的主体部分
        if(prime[i]>R)return;
        else{
            t=prime[i],x=L,y=R,xx=L-1,curn=num,temp=primcnt,curt=1;
            while(1){
     			curt++,temp+=primcnt;
				xx/=t,y/=t,x/=t;
				if(xx==y)break;
				curn*=t;
				search(i+1,temp,curn,x,y);
			}
			curt=1<<curt;
			if(primcnt<cnt/curt)return;
		}
	}
}
int main(){
    LL L,R;
    cin>>L>>R;
    prime_table();
    l=L,r=R;
    search(1,1,1,L,R);
    cout<<ans;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值