C++之路进阶——P2022
让我们来考虑1到N的正整数集合。让我们把集合中的元素按照字典序排列,例如当N=11时,其顺序应该为:1,10,11,2,3,4,5,6,7,8,9。
定义K在N个数中的位置为Q(N,K),例如Q(11,2)=4。现在给出整数K和M,要求找到最小的N,使得Q(N,K)=M。
输入输出格式
输入格式:
输入文件只有一行,是两个整数K和M。
输出格式:
输出文件只有一行,是最小的N,如果不存在这样的N就输出0。
题解
对于该题来说,我们只需考虑比K小的数就可以了,比K小的自然数中,比K小的字典序的个数=K-1。
eg:
对于456而言,从100~455 都可以,有456-100-1个。
从10~45 也可以 有(45-10-1) +1 //45是可以的,以为456还有后面的数,所以45也小于456(字典序)
从1~4中也都可以,有(4-1-1)+1//原因同上
由以上,我们便可以找出规律:比K字典序小的数等于ans=(K%10-1)//直到K=0;ans+=(t-1),因为除了位数与原数相同的的情况,等于是成立的,见以上标红部分。
规律找到,然后逐渐扩大N,以K的10^i扩大,当ans>m时,ans=(k*10^i-(ans-(M-1)+1))//减出多余的部分。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define ll long long 5 6 using namespace std; 7 8 ll N,M,K,cnt,base=1; 9 10 ll fj[21],s[21]; 11 12 void get(ll x) 13 { 14 ll t=0; 15 while (x){s[++t]=x%10;x/=10;base*=10;}base/=10; 16 for (int i=1;i<=t;i++) fj[i]=s[t-i+1]; 17 cnt+=t-1; 18 for (int i=t;i>=1;i--) 19 { 20 ll sum=0; 21 for (int j=1;j<=i;j++) 22 if (j!=1)sum*=10,sum+=fj[j]; 23 else sum*=10,sum+=fj[j],sum-=1; 24 cnt+=sum; 25 } 26 } 27 28 int main() 29 { 30 cin>>K>>M; 31 get(K); 32 if (cnt>=M||(K==base&&cnt<M-1)) {cout<<0<<endl; return 0;} 33 ll p=K-base,c=K; 34 for (;cnt<M-1;) 35 { 36 p*=10;c*=10; 37 cnt+=p; 38 } 39 N=max(K,c-(cnt-M+2)); 40 cout<<N<<endl; 41 return 0; 42 }
输入输出样例
输入样例#1:
Sample 1: 2 4 Sample 2: 100000001 1000000000 这里Sample 1 和 2是分开的两个数据点。
输出样例#1:
Sample 1: 11 Sample 2: 100000000888888879
说明
【数据约定】
40%的数据,1<=K,M<=10^5;
100%的数据,1<=K,M<=10^9。