题目:C2. Good Numbers (hard version)
题意:给出一个数n要求大于等于n的最小数(3的不同幂次相加)。
思路:比如可以是4=31+30,10=32+30;那么继续分析,n的数据范围是1018,只有logn的算法或者是通过题目推出公式才不会超时,然后看上面的例子,不难发现我们可以使用3进制将n表示出来,经过分析符合题意的数,其3进制只能为1或者0,再举个例子14=32+31+2*30;像这样,我们可以从高位开始遍历(保持它最小),找到第一个3进制位为2的(如果没有则表示这个数本身就是符合要求的数)。拿14来说它的3进制数是112,从高位到低位第三位为2,记录2所在的位置,从该标记位到高位遍历,为2则向高位进1.
AC代码:
#include <bits/stdc++.h>
#define ll long long
#define maxn 101
using namespace std;
int a[maxn];//记录进制位
int cou;
void threechange(ll x)
{
while(x>0){
a[++cou]=x%3;
x/=3;
}
}
ll mul(ll x,ll p)
{
ll ans=1;
for(int i=1;i<=p;i++){
ans*=x;
}
return ans;
}
int main()
{
int q;
scanf("%d",&q);
while(q--){
ll n;
memset(a,0,sizeof(a));
cou=0;
scanf("%lld",&n);
threechange(n);
int flag=0;
for(int i=cou;i>=1;i--){
//cout<<a[i]<<endl;
if(a[i]==2){
flag=i;//标记第一个进制位为2的位置
break;
}
}
ll sum=0;
for(int i=flag;i<=cou;i++){
if(a[i]==2){
a[i]=0;
a[i+1]++;
}
}
if(a[cou+1]!=0){cou++;}
for(int i=cou;i>=flag;i--){
//cout<<mul(3,i-1)<<" "<<a[i]<<" "<<cou<<endl;
sum+=a[i]*mul(3,i-1);
if(sum>=n)break;
}
printf("%lld\n",sum);
}
return 0;
}
如有疑问欢迎提出!