[ACNOI2022]进制转换

280 篇文章 1 订阅
"这篇博客探讨了一个数论问题,即如何在给定十进制数和数码限制下找到最大的合法进制。作者通过分析数位限制,提出了大胆的数学转换方法,摒弃了传统的整除分块策略,转而采用基于数位值的枚举。通过巧妙的数学推导,减少了搜索空间,并展示了高效的O(10log⁡10lln⁡y)mathcalO(10log_{10}
摘要由CSDN通过智能技术生成

题目

题目背景
我昨天还以为, H a n d I n D e v i l \sf HandInDevil HandInDevil日象 家的天才,不可能有人战胜它的 马桶白眼;今天的 D D ( X Y X ) \sf DD(XYX) DD(XYX) 的洞见就超越了它。他的体内,有着比人类更高明、更强大之物,炎翼鸟 也只是祂的宠物——祂是,普林斯普

“你的那双眼睛,比我的 马桶白眼 更厉害,” 质朴的 H a n d I n D e v i l \sf HandInDevil HandInDevil 浮现在 D D ( X Y X ) \sf DD(XYX) DD(XYX) 眼前,“现在 O n e I n D a r k \sf OneInDark OneInDark 正身处光明之中,而你能将他打入黑暗。”

题目描述
给出十进制数 y y y b b b 进制数 l l l,保证 l l l 的数码都在 [ 0 , 9 ] [0,9] [0,9] 中。你需要选择 b b b 使得 y ( 10 ) ⩾ l ( b ) y_{(10)}\geqslant l_{(b)} y(10)l(b) y ( b ) y_{(b)} y(b) 的每一位都在 [ 0 , 9 ] [0,9] [0,9] 之中。求最大的 b b b 。这里 x ( m ) x_{(m)} x(m) 表示将 x x x 的十进制表示 x = ∑ i ⩾ 0 a i 1 0 i    ( a i ∈ [ 0 , 9 ] ) x=\sum_{i\geqslant 0} a_i10^i\;(a_i\in[0,9]) x=i0ai10i(ai[0,9]) 重写为 m m m 进制下 x ( m ) = ∑ i ⩾ 0 a i m i x_{(m)}=\sum_{i\geqslant 0}a_im^i x(m)=i0aimi

数据范围与约定
为了避免某些问题,保证 10 ⩽ l ⩽ y ⩽ 1 0 18 10\leqslant l\leqslant y\leqslant 10^{18} 10ly1018 。数据组数 T ⩽ 7 × 1 0 4 T\leqslant 7\times 10^4 T7×104

思路

乍看之下,好像 y ⩾ l ( b ) y\geqslant l_{(b)} yl(b) 是很简单的,直接二分就得到了 b b b 的范围。然而下一个问题是:每个数位都在 [ 0 , 9 ] [0,9] [0,9] 该怎么翻译?

难以数位 d p \tt dp dp 的时候,不妨从数学的角度考虑。第 k k k 位上的数其实就是
⌊ y b k ⌋ − b ⌊ y b k + 1 ⌋ ⩽ 9 \left\lfloor{y\over b^k}\right\rfloor-b\left\lfloor{y\over b^{k+1}}\right\rfloor\leqslant 9 bkybbk+1y9

只能想到整除分块,对于 y ⩾ b 4 y\geqslant b^4 yb4 就用上式找到合法的 b b b,而 y < b 4 y<b^4 y<b4 就直接枚举 y ( b ) y_{(b)} y(b) 数位。时间复杂度较高。

优化点在于,可行的 b b b 其实非常非常少。上式中,很多整除分块的结果都是解不出一个合法的 b b b 的。我们只要能够充分剪枝就成功了。

第一个想法是,最高位的限制最强,所以我们要求 b k b^k bk 是严格最高位。但是这还不够强。问题还是在整除分块上——所以要大胆弃用原方法,转而 枚举该数位值。设为 a a a,则
a b k ⩽ y ⩽ a b k + 9 ∑ i = 0 k − 1 b i ab^k\leqslant y\leqslant ab^k+9\sum_{i=0}^{k-1}b^i abkyabk+9i=0k1bi

显然 b ⩾ 10 b\geqslant 10 b10 k ⩾ 1 k\geqslant 1 k1 。我们会惊讶地发现,可行的 b b b 的数量 少得可怜。因为后面简单放缩(或者直接用 b b b 进制数思考)可以发现
y ⩽ a b k + 10 b k − 1 y\leqslant ab^k+10b^{k-1} yabk+10bk1

也就是让它再进一位。那么,若找到最小的 b b b 使得原不等式成立,则 a ⋅ ( b + δ ) k ⩾ a b k + a k δ b k − 1 a\cdot(b+\delta)^k\geqslant ab^k+ak\delta b^{k-1} a(b+δ)kabk+akδbk1,在 δ ⩾ 10 a k \delta\geqslant{10\over ak} δak10 时就会超过 a b k + 10 b k − 1 ab^k+10b^{k-1} abk+10bk1 y y y 的上界,所以可行的 b b b 的数量只有 O ( δ ) = O ( 10 a k ) \mathcal O(\delta)=\mathcal O({10\over ak}) O(δ)=O(ak10) 个!

于是只需要找到 b m a x = y a k b_{max}=\sqrt[k]{y\over a} bmax=kay ,然后暴力判断 b ∈ [ b m a x − δ , b m a x ] b\in[b_{max}{\rm-}\delta,b_{max}] b[bmaxδ,bmax] 是否为解。时间复杂度几乎是 O ( 10 log ⁡ 10 l ln ⁡ y ) \mathcal O(10\log_{10}l\ln y) O(10log10llny) 的。

代码

即使分析出来很正确,还是需要剪枝卡常,这就很不厚道了……

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
#include <cmath>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
# define double (long double)
inline llong readint(){
	llong a = 0; int c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(const llong &x){
	if(x > 9) writeint(x/10);
	putchar(int((x%10)^48));
}

bool check(llong y, const llong &l, const llong &b){
	llong all = 0, v = 1;
	for(int d; y; y/=b){
		if((d = int(y%b)) > 9) return false;
		all += d*v, v *= 10;
	}
	return all >= l; // satisfying
}
inline llong qkpow(llong b, int q){
	llong a = 1; // no modules
	for(; q; q>>=1,b*=b) if(q&1) a *= b;
	return a;
}

llong powten[19];
int main(){
	rep(i,int(powten[0]=1),18) powten[i] = 10*powten[i-1];
	for(int T=int(readint()); T; --T){
		llong y = readint(), l = readint();
		for(int k=1; powten[k<<1]<y; ++k) rep(a,1,9){
			if(a+1 <= (l-1)/powten[k]) continue;
			llong b = llong(round(pow(double(y)/a,double(1)/k)+1));
			llong lb = llong(round(pow((y-(qkpow(b,k)
				-1)/(b-1)*9)/a,double(1)/k)-1));
			for(lb=max(lb,9ll); b!=lb; --b) // bottom line
				if(check(y,l,b)){ writeint(b); goto SCHEME; }
		}
		for(int b=100; true; --b) // bruteforce
			if(check(y,l,b)){ writeint(b); break; }
		SCHEME: putchar('\n');
	}
	return 0;
}

后记

据称,想到这个做法的原因是,一个数字的最高位对数字大小的影响最大。或许与本题有些相似,或许没有。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
.系统是为两个不同的用户服务的,所以功能要分成两个部分,一部分给管理员用,一部分给普通用户用. 2.为了信息的安全,所有使用系统的人必须是在数据库里有信息记录的,那就需要注册,然后用用户名密码进行登录,没有这些的人不能登录. 3.系统的界面的设计要合理,排版要科学,色彩搭配要合理,让人看了会感觉到体验很好,可以一眼就了解系统的整体布局. 4.每一个功能之间,不同操作者之间的功能衔接要好,底层代码要逻辑清晰,保证各个功能的响应时间. 5.系统里边有很多的文本输入功能,文本框大小要合理,文字要大小适中,最好可以自动检测格式和敏感词,在文本框上面要出相关说明. 6.网站要可以自适应,比如在电脑端和手机端分别打开系统,它的界面并不会发生错乱,会自动根据屏幕大小调整模块的布局。 基于b/s结构开发系统有诸多优势:b/s结构能够将许多不同语言、不同数据结构和不同操作系统等众多平台相结合,并在同一平台上实现多种功能模块。b/s结构更易于进行数据交换与共享。b/s结构提供了大量工具来帮助软件开发人员进行二次开发与维护工作,包括程序编译工具、应用程序编程工具和数据库访问技术等,它不但大大减少了软件开发人员的工作量,也使开发效率得到了提高。b/s结构上的应用程序和数据库之间有统一接口格式来进行通信与交互。进行通信和交互有以下优点: 2、灵活高效:用户可以在多个应用之间自由切换; 3、易于扩展:可以根据实际业务情况添加新功能模块,而不必对现有功能进行修改; 4、专业高效:系统操作简单方便; 5、节约成本:降低维护费用和运营费用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值