题目链接:http://codeforces.com/contest/948/problem/B
题目大意:有一个大于等于3的数X0,任意选择一个小于X0的质数P0,乘以一个数k使k*P0>=X0,且(k-1)*P0<X0,令X1=k*P0,任意选择一个小于X1的质数P1,乘以一个数u使u*P1>=X1,且(u-1)*P1<X1,令X2=u*P1;现在给你X2,求出所有满足条件的X0里最小的那一个。
解题思路:
绕,超级绕。
我一开始读题目的时候就觉得这道题很绕完全搞不清楚大小关系,经过神级学长的耐心讲解终于搞懂了,此处%红太阳。
首先我想到的是暴力解法,枚举出所有的X1,再枚举出X1最大的质因数,X1-P+1的最小值即为解。于是我们来算一下时间复杂度。枚举所有的X1->O(n),枚举出X1最大的质因数->O(sqrt(n)),n的范围在1e6,果断T。
于是寻找更省时间的方法,不用枚举X1或者不用枚举X1的质因数。
我们探究一下X1和X2的关系,发现如果从X2着手,是可以确定X1的范围的。X2等于一个小于等于X1的质数乘以一个正数,不妨设这个质数为P,那么X2-P<X1<=X2,右区间是固定死了的X2,左区间的最小值在P取最大值的时候取到。所以X1的范围是[X2-X2最大的质因数+1,X2]。
似乎没有什么用,还是要枚举,但是如果我们已知了X0呢?
假设我们有一个满足条件的X0,一定可以找到一个X1>=X0且处于这个区间(因为保证了X0是满足条件的一个解)。这个X1是如何找到的?一个小于等于X0的质数乘以一个正数!也就是说,对于任意大于X0且小于等于X2的数X0',这个质数都存在,那么这个X1也存在,那么当X1>=X0'的时候,我们X0‘也是合理解,当X1<X0'的时候,X0’必定处于这个区间!(X1处于这个区间,X0'小于等于X2)。而处于这个区间的所有数都是一个满足条件的X0(可以取自己的质因数乘以一个因子等于自己,那么X0==X1,X1处于区间内)。
于是我们得出结论:只要一个X0是满足条件的解,所有大于X0且小于等于X2的都是满足条件的解。
满足单调性->求最小->二分!
最后只要二分X0即可。
下面放31msAC代码:
1 #include<stdio.h> 2 #define MAXN 1000000 3 4 int prime[MAXN+5],tot; 5 bool f[MAXN+5]; 6 7 int _max(int a,int b){return a>b?a:b;} 8 9 void _before(){ 10 11 int i,j; 12 for(i=2;i<=MAXN;i++){ 13 if(!f[i])prime[++tot]=i; 14 for(j=1;j<=tot&&prime[j]*i<=MAXN;j++){ 15 f[i*prime[j]]=true; 16 if(i%prime[j]==0)break; 17 } 18 } 19 } 20 21 int main(){ 22 23 int x1,x2,l,r,i,mid,p; 24 bool flag; 25 _before(); 26 scanf("%d",&x2); 27 if(!f[x2]){ 28 printf("%d",x2); 29 return 0; 30 } 31 for(i=tot;i>=1;i--) 32 if(x2%prime[i]==0)break; 33 p=prime[i]; 34 l=2; r=x2; 35 while(l<r){ 36 if(l+1==r)break; 37 mid=(l+r)>>1; 38 flag=false; 39 for(i=1;prime[i]<=mid;i++){ 40 x1=(_max(mid,x2-p+1)+prime[i]-1)/prime[i]*prime[i];//取大于左端点的最小的数 41 if(x1-prime[i]<mid&&x1<=x2){ 42 flag=true; 43 break; 44 } 45 } 46 if(flag)r=mid; 47 else l=mid; 48 } 49 printf("%d",r); 50 return 0; 51 }