CF1601F Two Sorts 题解--zhengjun
link
这里提供一种不用 meet in middle 的方法,速度比较可观。
发现性质
开始简单的推一下式子。
于是,只需求出
由于
所以
转化问题
考虑枚举
对于
直接差分掉,问题转化为求出
再发现性质
对于位数相同的数
所以
发现
最后
于是,我们可以先枚举
然后二分出该位数下满足
还留下来最后一个问题,如何求出
计算
时间复杂度
总结步骤:
-
枚举
k=\lfloor\frac{rk_i-i}{p}\rfloor,O(\frac{n}{p}) -
枚举
i 的位数,O(\log n) -
二分
i ,O(\log n) -
计算
rk_i ,即枚举j 的位数,O(\log n)
总时间复杂度:
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using ll=long long;
const int N=20,p=998244353,mod=1e9+7;
char num[N];
int len;
ll n,pw[N];
int k,a[N];
ll getrk(ll x){
k=0;
for(ll y=x;y;y/=10)a[++k]=y%10;
for(int i=k+1;i<=len;i++)a[i]=0;
reverse(a+1,a+1+k);
ll now=0,ans=0;
for(int i=1;i<k;i++){
now=now*10+a[i];
ans+=now-pw[i-1]+1;
}
for(int i=k;i<len;i++){
now=now*10+a[i];
ans+=now-pw[i-1];
}
now=now*10+a[len];
if(now<=n)ans+=now-pw[len-1];
else ans+=n-pw[len-1]+1;
// fprintf(stderr,"getrk %lld %lld\n",x,ans+1);
return ans+1;
}
ll query(ll lim){
ll ans=0;
for(int i=1;i<=len;i++){
ll l=pw[i-1]-1,r=min(pw[i],n+1),mid;
for(;l+1<r;){
mid=(l+r)>>1;
if(getrk(mid)-mid<lim)l=mid;
else r=mid;
}
ans+=l-pw[i-1]+1;
}
// cerr<<"query "<<lim<<' '<<ans<<endl;
return ans;
}
signed main(){
scanf("%s",num+1),len=strlen(num+1);
for(int i=1;i<=len;i++)n=n*10+num[i]-'0';
for(int i=pw[0]=1;i<=len;i++)pw[i]=pw[i-1]*10;
ll las=0,ans=0;
for(ll k=-n/p-1;k*p<=n;k++){
ll cnt=query((k+1)*p);
ans-=k*(cnt-las),las=cnt;
}
cout<<(ans%mod*p%mod+mod)%mod;
return 0;
}