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

16

样例输入 #2

2016 100

样例输出 #2

42

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);
	}
}

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);
	}
}

其中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;
	}
}

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;
}

5.完整代码

#include<bits/stdc++.h>
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;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值