CF1870F Lazy Numbers 题解

CF1870F

题意:给一个长度为 n 的排列,求在其在 k 进制下按字典序排序后 [pi=i] 的值(n1018)。

直接做是不好办的,只能在一些数中找到 pi 的大小关系。

在手摸的过程中会发现一些长度相等的数之间会插入一些其它长度的数,很麻烦,考虑按长度分类。然后就可以发现 k 进制表示中长度均为 l 的数一定有 i<jpi<pj。考虑从这里下手。

把这个东西看作一个类似于分段函数的东西,fl(i)=pil 表示的是长度。其中 pi 的求解是简单的,从前往后扫一遍即可。

观察这个函数,发现肯定是递增的,那么在这个函数中符合条件的点一定在 xy=0 的直线上,容易发现如果有符合条件的点,一定只有一段区间内的点落在直线上,因为 fl(i) 始终 1

但这样并不好统计,显然的,因为 xy=0 的斜率为 1,令 gl(i)=fl(i)i,就变为了统计一个不降的序列的值为 0 的区间的长度。因为不能从前往后扫,二分两次即可。

代码:

typedef long long ll;
ll n,m;
ll pw[65],c;
ll calc(ll a,ll b,ll c){
	if(!b||!c)return 0;
	return (b<=a/c)?(b*c):a;
}
ll check(int len,ll mid){
	ll res=0,tmp=mid;
	for(int i=len;i>=0;i--)res+=tmp-pw[i]+1,tmp/=m;
	if(len==c)return res;
	for(int i=len+1;i<c;i++)res+=pw[i-len]*(mid-pw[len]);
	res+=calc(n+1,pw[c-len],mid)-pw[c];
	return res;
}
void solve(){
	cin>>n>>m;
	pw[c=0]=1;while(pw[c]<=n/m)pw[c+1]=pw[c]*m,c++;
	ll ans=0;
	for(int i=0;i<=c;i++){
		ll l=pw[i],r=((i==c)?n:(pw[i+1]-1)),L=r+1,R=l-1;
		while(l<=r){
			ll mid=(l+r)>>1;
			if(check(i,mid)>=mid)r=mid-1;
			else l=mid+1;
		}
		L=l;l=pw[i],r=((i==c)?n:(pw[i+1]-1));
		while(l<=r){
			ll mid=(l+r)>>1;
			if(check(i,mid)<=mid)l=mid+1;
			else r=mid-1;
		}
		R=r;
		ans+=max(0ll,R-L+1);
	}
	cout<<ans<<"\n";
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);
	int T;cin>>T;while(T--)solve();
	return 0;
}
posted @   Pengzt  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示