版权声明:转载请注明出处哦。 https://blog.csdn.net/Dch19990825/article/details/88624004
ZOJ Problem Set - 4085
补的很久以前一场比赛的题
题意:
设一个函数 :
代表将 ~ 所有数字按字典序排列,数字 的排列的位置是
现在给你一个 和 ,让你求最小的
思路:
-
如果 是 的 次方;那么下列讨论
- 如果 则答案为
- 否则 无解
-
先求出 ~ 之间的字典序小于k的个数;可以for循环求出来
-
如果个数 则无解
-
那么肯定有解,下面构造解
- 令
- 每次在 后面加一个 ,假设tot为这次在 后面添加 之前字典序小于 的个数。再次计算 ~ 小于 的个数
- 如果个数 ,则解在$k’ $之前,且解为 ( )
- 否则令 为此时 ~ 小于 的个数。返回第二步
-
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6;
ll k,m,n;
ll Pow10[20];
ll arr[20];
void init()
{
Pow10[0]=1,Pow10[1]=10;
for(int i=2;i<19;++i) Pow10[i]=Pow10[i-1]*10ll;
}
/*
计算1~k中比k小的数有多少个
排列组合的加法原理 一位数,二位数,三位数...len位数字 字典序小于k的个数之和
随后一直在k后面加0 计算比k小的数有多少个
*/
void calc()
{
ll mid=k;
int top=0;
do{
arr[++top]=mid%10;
mid/=10;
}while(mid);
ll Pretot=0;
ll Presum=0,ans=0;
/*特判10的次方*/
for(int i=0;i<=8;++i)
{
if(Pow10[i]==k)
{
if(m==i+1)
printf("%lld\n",k);
else
puts("0");
return ;
}
}
for(int i=top;i;--i)
{
Presum=Presum*10+arr[i];
Pretot+=Presum-Pow10[top-i];
if(i!=1)
Pretot++;
}
if(Pretot>m-1)
puts("0");
else if(Pretot==m-1)
printf("%lld\n",k);
else{
/*不停的往后面添加0,记录小于k的个数 并比较答案*/
for(;;)
{
top++;
Presum*=10ll;
if(Pretot+Presum-Pow10[top-1]>=m-1)
{
ans=Pow10[top-1]+m-1-Pretot-1;//还需添加m-1-ans个在Pow10(top-1)后面 因为有最后一个属包括0,所以把再次-1 表示把0算上
printf("%lld\n",ans);
return ;
/*找出答案*/
}
Pretot+=Presum-Pow10[top-1];
}
}
return ;
}
int main()
{
int t;
init();
scanf("%d",&t);
while(t--)
{
scanf("%lld %lld",&k,&m);
calc();
}
return 0;
}