original link - http://acm.hdu.edu.cn/showproblem.php?pid=6659
题意:
定义 F ( d , n ) F(d,n) F(d,n)为 1 , 2... n 1,2...n 1,2...n的十进制下位中,数字d出现的个数,找出小于等于 n n n的最大的数 x x x使得 F ( d , x ) = x F(d,x)=x F(d,x)=x。
解析:
神奇的做法,可惜比赛的时候在敲其他题目没有好好想过这题。
首先我们用数位 d p dp dp求出 F ( d , n ) = y F(d,n)=y F(d,n)=y,设 s u b = ∣ n − y ∣ sub=|n-y| sub=∣n−y∣,由于减少每个数至多只会减少 18 18 18个 d d d。所以我让 n n n往下缩小 ⌈ s u b 18 ⌉ \lceil\dfrac{sub}{18}\rceil ⌈18sub⌉。
这样可以保证不会漏掉正解。
复杂度证明:
以 d = 1 d=1 d=1为例,假定 n = a 1 ∗ 1 0 18 + a 2 ∗ 1 0 17 + . . . n=a_1*10^{18}+a_2*10^{17}+... n=a1∗1018+a2∗1017+...,则有: F ( 1 , n ) = ( b 1 ≤ a 1 ) 1 0 18 + b 2 ∗ 1 0 17 + . . F(1,n)=(b_1\leq a_1)10^{18}+{b_2}*10^{17}+.. F(1,n)=(b1≤a1)1018+b2∗1017+..
即 n = O ( k 1 x ) , F ( d , n ) = O ( k 2 x ) , ∣ n − F ( d , n ) ∣ = O ( k 3 x ) n=O(k_1x),F(d,n)=O(k_2x),|n-F(d,n)|=O(k_3x) n=O(k1x),F(d,n)=O(k2x),∣n−F(d,n)∣=O(k3x),每次减小步长是 O ( x ) O(x) O(x)级的。所以循环次数不会很大。
代码:
#include
using namespace std;
#define LL long long
LL ans;
int lim[20],up;
LL P10[20];
LL num[20];
LL ct(int len){
if(len<=0)return 0;
return 1ll*len*P10[len-1];
}
void dfs(int d,int pos){
if(pos==0)return;
for(int i=0;i<lim[pos];i++){
if(i==d)ans+=P10[pos-1];
ans+=ct(pos-1);
}
if(lim[pos]==d){
ans+=num[pos-1]+1;
}
dfs(d,pos-1);
}
void deal(int d,LL R){
ans=0;
up=0;
P10[0]=1;
while(R){
lim[++up]=R%10ll;
P10[up]=P10[up-1]*10ll;
R/=10ll;
}
for(int i=1;i<=up;i++){
num[i]=num[i-1]+lim[i]*P10[i-1];
}
dfs(d,up);
}
int main(){
int t;scanf("%d",&t);
while(t--){
int d;LL R;
scanf("%d%lld",&d,&R);
deal(d,R);
while(ans!=R){
R-=(abs(R-ans)+17ll)/18;
deal(d,R);
}
printf("%lld\n",R);
}
}