您的位置:首页 > 其它

NOIP 2016模拟赛1 T1 最多因数

2016-10-15 23:04 302 查看

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整除的数,也即是从L到R中至少有一个数能被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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: