H
题目描述
所有含有2或3或6的数字都定义为"快乐数”,给出一个n,求第n小的"快乐数”是多少。
~
题意分析
赛时思路:
一开始我看错题了,以为n是范围,想打表来着…… 后来一看题,打表是不可能了,就考虑有没有正解。
首先,对于每个"快乐数”都有一个共性:每个数都由2 3 6组成!
有什么发现没有?是不是有点像进制转换问题?
但是在实现代码的时候,我发现一点不一样的东西:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
"快乐数” | 2 | 3 | 6 | 22 | 23 | 26 | 32 | 33 | 36 | 62 |
三进制 | 1 | 2 | 10 | 11 | 12 | 20 | 21 | 22 | 100 | 101 |
我们很明显的发现,出现问题了,因为0的性质十分的特殊,在进位的时候不能放在第一个。
然后这个思路pass掉了。
但是这个思路虽然不是正规的三进制,但我们可以用1 2 3(分别代表2 3 6)把进制转换的过程模拟出来!
很明显单纯枚举的话是不现实的,所以我们按照进制转换的运算规则,来确定这个数字的位数。
那么怎么确定位数呢?
乘法原理。
注意到对于n位数,每一位都有三种可能的填数方法:2 3或6。所以n位数就有3^n 种可能性。
那么如何根据n来确定位数呢?需要从1位数开始累加可能性,也就是说,等比数列求和。
为了防止被卡常,所以我提前打了个表。
```const int log3[]={0,3,12,39,120,363,1092,3279,9840,29523,88572,265719,797160,2391483,7174452,21523359,64570080,193710243,581130732};
然后我们找完多少位了,接下来我是想用n减去区间下限,然后依次从1开始枚举,模拟这一种特殊的进制运算。
然后就TLE了。
后来我又一想,依旧是可以使用进制转换的方法。
n减去区间下限以后,是这个数字在这个位数区间里面的序号。
也就是说,把这个序号转化成三进制,然后再累加到求出那个位数对应的最小数(全是11)上面。
重点,由于序号1是它本身,所以我们应当对序号采取减一操作。
然后我们就得到了一串数,只含有1 2和3。
然后再用2 3 6进行替换即可。
AC CODE (O(log3 n))
```#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,wei,a[10000],b[10000];
const int log3[]={0,3,12,39,120,363,1092,3279,9840,29523,88572,265719,797160,2391483,7174452,21523359,64570080,193710243,581130732};
void f(int x)
{
int wei2=wei;
while(x)
{
b[wei2]=x%3;
x/=3;
wei2--;
}
}
signed main()
{
cin>>n;
for(int i=0;;i++)
if(n>log3[i]&&n<=log3[i+1])
{
wei=i+1;
n-=log3[i];
break;
}
for(int i=1;i<=wei;i++)
a[i]=(int)1;
n--;
f(n);
for(int i=1;i<=wei;i++)
a[i]+=b[i];
for(int i=1;i<=wei;i++)
{
if(a[i]==(int)1)
cout<<2;
if(a[i]==(int)2)
cout<<3;
if(a[i]==(int)3)
cout<<6;
}
return 0;
}