浏览器标题切换
浏览器标题切换end

CodeForces-1249C2-Good Numbers (hard version) -巧妙进制/暴力

The only difference between easy and hard versions is the maximum value of n.

You are given a positive integer number nn. You really love good numbers so you want to find the smallest good number greater than or equal to n.

The positive integer is called good if it can be represented as a sum of distinct powers of 33 (i.e. no duplicates of powers of 33 are allowed).

For example:

  • 3030 is a good number: 30=33+3130=33+31,
  • 11 is a good number: 1=301=30,
  • 1212 is a good number: 12=32+3112=32+31,
  • but 22 is not a good number: you can't represent it as a sum of distinct powers of 33 (2=30+302=30+30),
  • 1919 is not a good number: you can't represent it as a sum of distinct powers of 33 (for example, the representations 19=32+32+30=32+31+31+31+3019=32+32+30=32+31+31+31+30 are invalid),
  • 2020 is also not a good number: you can't represent it as a sum of distinct powers of 33 (for example, the representation 20=32+32+30+3020=32+32+30+30 is invalid).

Note, that there exist other representations of 1919 and 2020 as sums of powers of 33 but none of them consists of distinct powers of 33.

For the given positive integer n find such smallest m (nm) that m is a good number.

You have to answer qq independent queries.

Input

The first line of the input contains one integer q(1q500) — the number of queries. Then qq queries follow.

The only line of the query contains one integer  n(1n1018).

Output

For each query, print such smallest integer m (where nm) that m is a good number.

Example

Input
8
1
2
6
13
14
3620
10000
1000000000000000000
Output
1
3
9
13
27
6561
19683
1350851717672992089

题意:
对于每组数据, 给出一个n,要求求出大于等于n的m,m需要满足的条件是:由3的次方相加组成,且每个3的次方最多只能出现一次。

注意:
对于次方问题,最好用快速幂进行运算,运用pow会造成奇怪错误。

法一:三进制
思路:
由于求的答案与3的次方有关,所以可以把每一个给出来的n转换成三进制存到数组里再去进行判断,
比如给出一个n=14,转换成三进制的话就是112,从高位往低位进行判断,
如果存放进制的数组里面只含有1和0的话,说明该数是3的次方,直接输出即可
否则可以进行判断,从低位往高位找,找到第一个2记录下标且break
 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<string.h>
 4 using namespace std;
 5 const int N=1e4+20;
 6 typedef unsigned long long ull;
 7 
 8 ull a[N];
 9 
10 ull ksm(ull x,ull n)
11 {
12     ull res=1;
13     while(n>0)
14     {
15         if(n&1)
16             res=res*x;
17         x=x*x;
18         n>>=1;
19     }
20     return res;
21 }
22 
23 int main()
24 {
25     int t;
26     ull n;
27     scanf("%d",&t);
28     while(t--)
29     {
30         memset(a,0,sizeof(a));
31         scanf("%llu",&n);
32         int p=0;
33         ull ans=n;
34         while(n)
35         {
36             a[p++]=n%3;
37             n=n/3;
38 
39         }
40 //        for(int i=0;i<p;i++)
41 //            printf("%d",a[i]);
42 //        printf("\n");//211
43         //从低位往高位找,找到第一个2记录下标且break
44         int k=-1;
45         for(int i=p-1;i>=0;i--)
46         {
47             if(a[i]==2)
48             {
49                 k=i;
50                 break;
51             }
52         }
53         if(k==-1)
54         {
55             printf("%llu\n",ans);
56             continue;
57         }
58 //        printf("%d***\n",k);
59         for(int i=k;i<p;i++)
60         {
61             if(a[i]==2)
62             {
63               a[i]=0;
64               a[i+1]++;
65             //  break;
66 //            printf("%d***%d\n",i+1,a[i+1]);
67             }
68         }
69 
70         if(a[p]!=0)
71             p++;
72         ull sum=0;
73         for(int i=p-1;i>=k;i--)
74         {
75              sum=sum+a[i]*ksm(3,i);
76         }
77         printf("%llu\n",sum);
78 
79     }
80     return 0;
81 }

 

 

法二:暴力

思路:

  • 首先数据给出的范围是1018,所以可以通过打表计算出3的几次方会超过该范围,打表得出是38,所以数字开到40就ok
ll sum=1;
int k;
for(int i=1;i<N;i++)
{
    sum=sum*3;
    if(sum>=1e18)
    {
        printf("%d\n",i);//i==38
        break;
    }
}
  • 把3的次方存入a数组中。                                                                                                                                                   
  • 再来,开始分析题意,题意要求求的是满足条件最小的m,所以如果将a数组从小往大遍历的话,找到的m不一定是最小的,所以需要将a数组从大往小遍历,再把小于等于n的第一个数减掉。
  • 还需要知道的一点就是30+31+32+...+3n-1<3n,所以最后再从前往后遍历,找到没有被标记过的数,注意30需要特判。
 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<string.h>
 4 using namespace std;
 5 const int N=1e4+20;
 6 typedef long long ll;
 7 
 8 ll a[50];
 9 bool book[N];
10 
11 ll ksm(ll x,ll n)
12 {
13     ll res=1;
14     while(n>0)
15     {
16         if(n&1)
17             res=res*x;
18         x=x*x;
19         n>>=1;
20     }
21     return res;
22 }
23 
24 int main()
25 {
26     for(int i=0; i<=38; i++)
27         a[i]=ksm(3,i);
28     int t;
29     ll n;
30     scanf("%d",&t);
31     while(t--)
32     {
33         memset(book,0,sizeof(book));
34         scanf("%lld",&n);
35         ll sum=0;
36         while(1)
37         {
38             int flag=0;
39             for(int i=38; i>=0; i--)
40             {
41                 if(a[i]<=n&&book[i]==0)//第一个找到的就是最大的
42                 {
43                     sum+=a[i];
44                     flag=1;
45                     book[i]=1;
46                     n-=a[i];
47                     break;
48                 }
49             }
50             if(flag==0)
51                 break;
52         }
53         if(n==0)
54         {
55             printf("%lld\n",sum);
56             continue;
57         }
58         ll kk;
59         int k;
60         for(int i=1;i<=38;i++)
61         {
62             if(book[i]==0)
63             {
64               kk=a[i];
65               k=i;
66               break;
67             }
68         }
69         sum=sum+kk;
70         for(int i=0;i<k;i++)
71             sum-=a[i];
72         printf("%lld\n",sum);
73     }
74     return 0;
75 }

 



posted @ 2019-10-30 23:23  抓水母的派大星  阅读(197)  评论(0编辑  收藏  举报