绍兴一中模拟赛3.22——踟躇(chíchú)而过

博客围绕计算大于等于t且满足f(x,k)=s的数展开。介绍了从高位到低位贪心填数的next操作,复杂度为O(logn)。通过设置g[s],不断更新寻找合法答案。分析了该做法的可行性及复杂度上界约为O(snlogn),实际实现速度较快。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description


n&lt;=1018n&lt;=10^{18}n<=1018,保证gcd(a,b)=1gcd(a,b)=1gcd(a,b)=1

Solution

考虑计算大于等于ttt的第一个满足f(x,k)=sf(x,k)=sf(x,k)=s的数
我们可以从高位到低位贪心去填,每一位从零开始依次尝试,看剩余位用剩余数字去填能否大于等于 ttt
我们设这个过程叫next(t,k,s)next(t,k,s)next(t,k,s)
因为答案不会超过1e181e181e18,经过xjbxjbxjb计算后得出sss 最多只有400400400
单次nextnextnext操作的复杂度可以做到O(logn)O(logn)O(logn)
g[s]g[s]g[s] 表示大于等于nnn的满足 f(x,a)=f(x,b)=sf(x,a)=f(x,b)=sf(x,a)=f(x,b)=s 的整数xxx
初始时 对于所有sssg[s]=ng[s]=ng[s]=n
然后每次找最小的 g[s]=tg[s]=tg[s]=t,令w=max(next(t,a,s),next(t,b,s))w=max(next(t,a,s),next(t,b,s))w=max(next(t,a,s),next(t,b,s))
如果t=wt=wt=wttt就是一个合法答案,显然这种做法下这是最优答案,直接输出
否则将g[s]g[s]g[s]设为www,重复上述过程
为什么这样做是可以的?
我们考虑对于一个区间[l,r][l,r][l,r]满足f(x,a)=sf(x,a)=sf(x,a)=s的数和f(x,b)=sf(x,b)=sf(x,b)=s数集,将他们排序之后会形如 a,b,a,a,a,b,b,a,aa,b,a,a,a,b,b,a,aa,b,a,a,a,b,b,a,a等,发现求nextnextnext的过程相当于求有多少a,ba,ba,b交替出现,如上例就变成了a,b,a,b,aa,b,a,b,aa,b,a,b,a,只进行了555nextnextnext操作
显然交替次数的级别不会高于a,ba,ba,b任意一种数的出现的次数,由于进位的存在使得这两种数出现趋向随机,而根据生日悖论,出现矛盾的位置一般不超过出现次数的平方,所以寻找次数的上界显然为O(n)O(\sqrt n)O(n)
于是总的复杂度上界约为O(snlogn)O(s\sqrt nlogn)O(snlogn)
看起来过不去
但是对于不同的sss它的寻找次数会有较大差异,对于有些sss寻找次数会非常少,加上求nextnextnext的常数较小,最终实现起来的速度是很快的

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pi;
#define mp make_pair
#define fi first
#define se second
priority_queue<pi,vector<pi>,greater<pi> >q;
pi x;
ll xx,n,ans,tmp;
int cnt,sum,a[70],b[70],ss,i,A,B;
ll ne(ll x,int k,int s){
	memset(b,0,sizeof(b));
	xx=x;cnt=sum=0;
	while (xx) sum+=(a[++cnt]=xx%k),xx/=k;
	if (sum==s) return x;cnt++;
	a[cnt]=0;
	for (int i=cnt;i;i--){
		s-=a[i],b[i]=a[i],ss=s;
		int j=i-1;
		for (;j;j--){
			if (ss>a[j] && a[j]<k-1) break;
			if (ss<a[j]){
				j=0;
				break;
			}
			ss-=a[j];
		}
		if (!j){
			s--,b[i]++;
			break;
		}
	}
	for (int i=1;s;i++){
		if (i>cnt) return 1e18;
		if (s>=k-1-b[i]) s-=k-1-b[i],b[i]=k-1;
		else b[i]+=s,s=0;
	}
	ans=0;
	for (int i=cnt;i;i--) ans=ans*k+b[i];
	return ans;
}
int main(){
	scanf("%lld%d%d",&n,&A,&B);
	if (!n) return puts("0"),0;
	for (i=1;i<=400;i++) q.push(mp(max(ne(n,A,i),ne(n,B,i)),i));
	while (1){
		x=q.top(),q.pop();
		tmp=max(ne(x.fi,A,x.se),ne(x.fi,B,x.se));
		if (x.fi==tmp) return printf("%lld",tmp),0;
		q.push(mp(tmp,x.se));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值