• BZOJ 4612([Wf2016]Forever Young) 题解


    1.题目

    题目描述

    我的生日快到了!唉,但如今我已经老了,我想重新获得年轻的感觉。
    幸运的是,我想出了一个让人感觉更年轻的好方法:如果我把我的年龄以适当选择的 b b b进制书写,那么它看起来会更小。例如,假设我以 10 10 10进制书写的年龄是 32 32 32岁;以 16 16 16进制书写,它只有 20 20 20( 0 x 20 0x20 0x20)!
    然而,在这样做时,我不能选择任意进制数。如果以 b b b进制为数写的我的年龄包含 0 − 9 0-9 09以外的数字,那么很明显我在作弊,这违背了目的。此外,如果我的年龄的进制数 b b b太小,那么很明显我在作弊。
    在我希望我的年龄看起来有多小的问题上,考虑到我的年龄 y y y和下限 l l l,找到最大的进制数 b b b,这样写在基数 b b b中的 y y y只包含十进制数字,并且当将其看做一个十进制数字时至少是 l l l

    输入格式

    输入由一行组成,其中包含两个 10 10 10进制的整数 y y y ( 10 ≤ y ≤ 1 0 18 10\le y\le10^{18} 10y1018 –是的,我很老)和 l l l( 10 ≤ l ≤ y 10\le l \le y 10ly)

    输出格式

    如上所述,输出最大进制数 b b b

    说明/提示

    时间限制: 1 s 1s 1s,内存限制: 1 G B 1GB 1GB

    样例输入 #1

    32 20
    
    • 1

    样例输出 #1

    16
    
    • 1

    样例输入 #2

    2016 100
    
    • 1

    样例输出 #2

    42
    
    • 1

    2.思路

    我们考虑枚举y转成b进制时的那个数(记为 x x x)。
    首先考虑到 x x x 不用枚举太多,因为当 x x x 太大时, b b b 会很小,那时只需要枚举 b b b 就行了。
    经过本人的多次测试: x x x 只要枚举到 1000 1000 1000 ,剩下到靠枚举 b b b 即可。
    接下来我们考虑枚举 x x x 怎么做:
    ①: x x x 是一个两位数,设它是 a 1 × 10 + b 1 a_1\times 10+b_1 a1×10+b1,则我们可以解一个一元一次方程: a 1 ⋅ b + a 2 = x a_1\cdot b+a_2=x a1b+a2=x,可以轻松解出 b b b
    ②: x x x 是一个三位数,设它是 a 1 × 1 0 2 + a 2 × 10 + a 3 a_1\times 10^2+a_2\times 10+a_3 a1×102+a2×10+a3,则我们可以解一个一元三次方程: a 1 ∗ b 2 + a 2 ∗ b + a 3 = x a_1*b^2+a_2*b+a_3=x a1b2+a2b+a3=x,即 a 1 ∗ b 2 + a 2 ∗ b + ( a 3 − x ) = 0 a_1*b^2+a_2*b+(a_3-x)=0 a1b2+a2b+(a3x)=0,利用求根公式 b = − b ± b 2 − 4 ⋅ a ⋅ c 2 ⋅ a b=\frac{-b±\sqrt{b^2-4\cdot a\cdot c}}{2\cdot a} b=2ab±b24ac ,可以快速求出根,但是注意要使用__int128long double
    这样枚举 x x x 就完成了。
    接下来考虑枚举 b b b 怎么做:
    b b b1e6 开始往下枚举,每次都将 y y y 转换成 b b b 进制,然后判断是否有一位大于 9 9 9,最后再和 l l l 比较一下即可。

    3.代码

    1.解一元一次方程部分

    for(register int i=max(l,10ll);i<=99;++i){
    	int a=i/10,b=i%10;//找ap+b=y 
    	if((y-b)%a==0){
    		ans=max(ans,(y-b)/a);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.解一元二次方程部分

    for(register int i=max(l,100ll);i<=999;++i){
    	Int a=i/100,b=i/10%10,c=i%10-y;//求a*p^2+b*p+c=0
    	Int nick=b*b-4*a*c;
    	if(nick<0)
    		continue;
    	nick=Sqrt(nick);
    	Int p1=-b+nick,p2=-b-nick;
    	p1=p1/(2*a),p2=p2/(2*a);
    	for(int pp=1;pp<=2;pp++)
    	{
    		Int L=p1-20,R=p1+20;
    		if(L<10)
    			L=10;
    		for(Int xp=L;xp<=R;++xp){
    			Int sum=a*xp*xp+b*xp+c;
    			if(sum==0){
    				if(xp>ans)
    					ans=xp;
    				break;
    			}
    		}
    		swap(p1,p2);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    其中Int表示__int128

    3.暴力枚举部分

    for(register int i=1e6;i>=10;--i){
    	if(i<ans){
    		break;
    	}
    	zhuan(i);
    	if(!check())
    		continue;
    	if(cmp()){
    		ans=max(ans,i);
    		break;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.其余函数部分

    inline void zhuan(int x){
    	cct=0;
    	int p=y;
    	while(p){
    		cc[++cct]=p%x;
    		p/=x;
    	}
    	for(register int i=1;i<=cct/2;++i)
    		swap(cc[i],cc[cct-i+1]);
    	return;
    }
    inline bool check(){
    	for(register int i=1;i<=cct;++i){
    		if(cc[i]>9)
    			return false;
    	}
    	return true;
    }
    inline bool cmp(){
    	int ans=0;
    	for(register int i=1;i<=cct;++i)
    		ans=ans*10+cc[i];
    	if(ans>=l)
    		return true;
    	else
    		return false;
    }
    Int Read(){
        Int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)&&ch!='-')ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return f*x;
    }
    void print(Int x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)print(x/10);
        putchar(x%10+'0');
    }
    Int Sqrt(Int x){
    	Int L=1,R=1000000000000000;
    	while(L<R){
    		Int mid=L+R;
    		mid=mid/2;
    		if(mid*mid<x){
    			L=mid+1;
    		}
    		else{
    			R=mid;
    		}
    	}
    	for(Int i=L-5;i<=L+5;++i){
    		if(i*i<=x&&(i+1)*(i+1)>x){
    			return i;
    		}
    	}
    	return -1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    5.完整代码

    #include
    using namespace std;
    #define int long long
    #define Int __int128
    #define doudle long double
    inline int read();
    int y,l;
    int cc[1377];
    int cct;
    int ans;
    inline void zhuan(int x){
    	cct=0;
    	int p=y;
    	while(p){
    		cc[++cct]=p%x;
    		p/=x;
    	}
    	for(register int i=1;i<=cct/2;++i)
    		swap(cc[i],cc[cct-i+1]);
    	return;
    }
    inline bool check(){
    	for(register int i=1;i<=cct;++i){
    		if(cc[i]>9)
    			return false;
    	}
    	return true;
    }
    inline bool cmp(){
    	int ans=0;
    	for(register int i=1;i<=cct;++i)
    		ans=ans*10+cc[i];
    	if(ans>=l)
    		return true;
    	else
    		return false;
    }
    Int Read(){
        Int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)&&ch!='-')ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return f*x;
    }
    void print(Int x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)print(x/10);
        putchar(x%10+'0');
    }
    Int Sqrt(Int x){
    	Int L=1,R=1000000000000000;
    	while(L<R){
    		Int mid=L+R;
    		mid=mid/2;
    		if(mid*mid<x){
    			L=mid+1;
    		}
    		else{
    			R=mid;
    		}
    	}
    	for(Int i=L-5;i<=L+5;++i){
    		if(i*i<=x&&(i+1)*(i+1)>x){
    			return i;
    		}
    	}
    	return -1;
    }
    signed main(){
    	y=read(),l=read();
    	for(register int i=max(l,10ll);i<=99;++i){
    		int a=i/10,b=i%10;//找ap+b=y 
    		if((y-b)%a==0){
    			ans=max(ans,(y-b)/a);
    		}
    	}
    	for(register int i=max(l,100ll);i<=999;++i){
    		Int a=i/100,b=i/10%10,c=i%10-y;//求a*p^2+b*p+c=0
    		Int nick=b*b-4*a*c;
    		if(nick<0)
    			continue;
    		nick=Sqrt(nick);
    		Int p1=-b+nick,p2=-b-nick;
    		p1=p1/(2*a),p2=p2/(2*a);
    		for(int pp=1;pp<=2;pp++)
    		{
    			Int L=p1-20,R=p1+20;
    			if(L<10)
    				L=10;
    			for(Int xp=L;xp<=R;++xp){
    				Int sum=a*xp*xp+b*xp+c;
    				if(sum==0){
    					if(xp>ans)
    						ans=xp;
    					break;
    				}
    			}
    			swap(p1,p2);
    		}
    	}
    	for(register int i=1e6;i>=10;--i){
    		if(i<ans){
    			break;
    		}
    		zhuan(i);
    		if(!check())
    			continue;
    		if(cmp()){
    			ans=max(ans,i);
    			break;
    		}
    	}
    	printf("%llu\n",ans);
    	return 0;
    }
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
  • 相关阅读:
    剑指offer 14. 二进制中1的个数
    mybatis基础02
    FileInputStream文件字节输入流
    CleanMyMac X中文---一键优化Mac,释放存储空间新利器
    15.springmvc源码解读之手写springmvc(简易版本)
    TrOCR – 基于 Transformer 的 OCR 入门
    二维码制作教程:如何制作一个文件二维码?
    2022不容错过的50个“低代码”发展现状、趋势与数据统计
    Linux Jar包定时重启脚本,按最新时间的Jar包启动
    redis初级介绍
  • 原文地址:https://blog.csdn.net/yyf525/article/details/127131333