题目:
Problem - 1811E - Codeforceshttps://codeforces.com/problemset/problem/1811/E
题意:给一个不含数字四的序列,求出第n位置的数为多少;
input
7
3
5
22
10
100
12345
827264634912
output
3
6
25
11
121
18937
2932285320890
一.二分答案
该思路是比较容易想到的思路
思路:可以反着思考,要是给你一个数可以较容易求得这个数为该序列的第几位,然后再采用二分求解;
1.便可以采用二分答案的思维进行求解,主要是check函数的构建,求一个数为在第多少位置,一共可分为三种情况,最大位数的数字大于4,等于4,和小于4三种情况。
if(st>4)//最高位大于4,则需减去最高位为四的特殊情况
{
y=y-m-(st-1)*a[i-1];
}
else if(st==4)//若刚好等于4,则减去4区间含有4的数,并将4后的数全部删掉,并退出循环
{
y=y-(st)*a[i-1]-(x-4*m+1);
break;
}
else//小于4,则只需减去每个区间的含有四的数
{
y-=st*a[i-1];
}
2.每次对该数进行减去最大位数的数字进行求解,就是利用每个固定区间内含有四的数字为固定值,用一个数组保存该值,并有递推公式为a[i]=a[i-1]*9+pow(10,i-1);
3.以10做为单位,每多一位的10位里就包含9个上一级的数字以及上一级的本身大小,因为那一位的最大位上数字为4,所以得全部加里面;
3.例如:1到10大小区间内就只有1个含有四的数,而1到100大小区间为9*1+10;
再例如:求解x=521在该序列中的第几(y)位:
y最初也为521;
先判断最高位5:大于4,则需要521减去0-99,100-199,200-299,300-399,400-499中含有4 的个数,前四个分段里的数都为都与1-100中含有4的数相等,即减去19*4(之前通过递推式进行求得),最后一个为特殊情况都含有4,再减去100,则y=345;并去掉最高位将x变为21;
再判断更新后的最高位为2,区间为0-9,10-20;同样则减去之前求得的数组中两位数包含四的值,y=y-1*2=343;再将x变为1;
再判断1,0-1,无含有四的数则退出;
变求得x=521的数在该序列中为第343个数;
4.而check函数主要是求解一个数在该序列的第几位,循环该数的位数,减去所对应情况的含有四的数字个数,若比原数较大则需往较小的数字查找;
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<math.h>
#define MAXN 110
using namespace std;
typedef long long ll;
int add(int x){return x<0?-x:x;}
ll a[200],p[200];
ll check(ll x)//求解x该数在该序列中的位置
{
ll n=0,i;
ll y=x;
while(y)//求该数有几位,后面方便从前往后搜寻
{
n++;
y/=10;
}
y=x;
for(i=n;i>=1;i--)
{
ll m=p[i-1];
ll st=x/m;
ll yuan=y;
if(st>4)//最高位大于4,则需减去最高位为四的特殊情况
{
y=y-m-(st-1)*a[i-1];
}
else if(st==4)//若刚好等于4,则减去4区间含有4的数,并将4后的数全部删掉,并退出循环
{
y=y-(st)*a[i-1]-(x-4*m+1);
break;
}
else//小于4,则只需减去每个区间的含有四的数
{
y-=st*a[i-1];
}
x-=m*st;//更新x值,去掉刚刚计算的最高位
if(x==0)//若变为0,则直接退出,说明后面全为0
break;
}
return y;
}
ll Find(ll x)//二分查找
{
ll l=1,r=1e15;
while(l<r)
{
ll mid=(l+r)/2;
if(check(mid)>=x)//若mid该数在该序列中的位置大于所查找的数,则向前查找
{
r=mid;
}
else{ //若小于则向后
l=mid+1;
}
}
return l;
}
int main()
{
ll i,n,t,x1,x2,y1,y2;
a[1]=1,p[1]=10,p[0]=1;
for(i=2;i<=16;i++)//求得在每个区间中含有数字4的数
{
a[i]=a[i-1]*9+pow(10,i-1);
p[i]=p[i-1]*10;
}
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
ll ans=Find(n);
printf("%lld\n",ans);
}
return 0;
}
二.九进制求解
思路:采用九进制求解,由于在十进制中少了一个数,所以便可转化为9进制,转化后对每位上进行分开存储,若大于等于4则需进行加一操作(因为没有四这个数)
例如:100转化为9进制后为121,则121即是所求解的答案
再求解之前那个例子:343转化为9进制后为421,将大于等于4的数进行+1,则为521即为所求;
在10进制中含有0,1,2,3,4,5,6,7,8,9十个数,
9进制下不含四有:0,1,2,3,5,6,7,8,9;
所以比较好理解在进行转化为9进制后数在十进制下看数会变大,其实可以倒过来理解,比如121该数在该序列中在100的位置,少了一位数的值在看成九进制后再转化为十进制后就是在该序列的第几位
#include<stdio.h>
#define ll long long
ll k,a[100];
int main()
{
int t,i;
scanf("%d",&t);
while(t--)
{
scanf("%lld",&k);
int n=0;
while(k)//转化为九进制并进行分开存储
{
a[++n]=k%9;
k/=9;
}
for(i=1;i<=n;i++)//对大于等于4的数进行加一操作
{
if(a[i]>=4) a[i]++;
}
for(i=n;i>=1;i--)//由于在转化的时候是倒序存储的,则输出需从后往前输出
printf("%d",a[i]);
printf("\n");
}
return 0;
}