目录
题目:
(还是想说vj的页面看着亲切)
题目大意:定义“好数”为3的幂的和,并且指数要不相同,给你一个数x,求>=x的最小“好数”
思路:
数字高达1e18,又是“幂的和”,这一连串的信息,窝居然没有想到“进制”QAQ,那么在了解要基于“三进制”来做,那么具体该怎样实现呢?拖拖拉拉可算弄懂了三种方法,在此记录一下,感谢雪长!感谢雪长感谢雪长!!!
一、三进制标书
第一种最容易接受,从低位向高位遍历,如果这一位是0或1,则这一位不变,如果超过1则这一位变成0(因为指数不能重复,即这个幂最多只选一次),下一位+1。
注意的是,从低到高位进一处理后,后面的都要置0,否则不是最小的。
举个栗子:十进制数7,转成三进制是21,进一操作处理后是101(对应十进制10),而我们要的是100(对应十进制9)
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
int t;
ll n,a[40];
void solve(){
cin >>n;
ll tp=n,ans=0;
int k=0,f=-1;
memset(a,0,sizeof(a));
while(tp){
a[k++]=tp%3;
tp/=3;
}
for(int i=0;i<k;i++){
if(a[i]>1){
f=i;
a[i]=0;
a[i+1]++;
}
}
if(f!=-1){
for(int i=0;i<f;i++)a[i]=0;//一路进一后最低位的0变成1,其后所有的都置0
}
tp=1;
for(int i=0;i<=k;i++){
ans+=tp*a[i];
tp*=3;
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >>t;
while(t--){
solve();
}
return 0;
}
二、二分的思路
第二种是二分思路,二进制的表示但是位权是3,这样的表示在定义域上不连续但是单增,所以可以二分.其实就是在暴力的基础上用了二分提速,一开始不懂的是get函数,它是把x分解成2进制,x就是在这样的表示下的第几个数,分解了以后就可以知道第x个数对应十进制是多大。这样的写法是判x的第i位是不是1,在这样的表示下,第1 即个数是1,第2 即个数是3,第3即 个数是4,第4即个数是9......
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
using namespace std;
typedef long long LL;
LL n,a[40];
LL get(LL x){
LL res=0;
for(int i=0;i<=39;i++){//二进制形式下的x第i位是否为1(取)
if((x>>i)&1)res+=a[i];
}
return res;
}
void solve(){
cin>>n;
LL l=1,r=(1ll<<39)-1;
while(l<r){
LL mid=l+r>>1;
if(get(mid)>=n) r=mid;
else l=mid+1;
}
cout<<get(l)<<endl;
}
int main(){
IOS;
a[0]=1;
for(int i=1;i<=39;i++)a[i]=a[i-1]*3;
int t;cin>>t;
while(t--){
solve();
}
return 0;
}
三、贪心
第三种方法是贪心的思路,“先从大到小贪,如果贪完了还剩下一点,再加上符合条件能构造出的最小的值”,相当于二进制加一,111+1=1000,就是把后面的1改成0在前面的0改1.在此珍藏U老师的神图,感谢雪长感谢雪长感谢雪长!(你永远可以相信U老师~)
下面第一篇题解用的vis数组模拟二进制,“其实也没有必要这样构造,直接贪心完把选的位数的二进制表示加一再算出来和就是答案了”,于是第二篇代码就用一个长整型的x来代替vis数组的作用。
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
int t,ct=1,vis[40];
ll a[40],n;
void init(){
a[0]=1;
while(1){
a[ct]=a[ct-1]*3;
if(a[ct]>1e18)break;
ct++;
}
}
void solve(){
cin>>n;
memset(vis,0,sizeof(vis));
ll sum=0,tp=n;
for(int i=ct;i>=0;i--){
if(tp>=a[i]){
vis[i]=1;
tp-=a[i];
sum+=a[i];
}
}
if(sum==n){cout<<n<<'\n';return;}
for(int i=0;i<=ct;i++){
if(vis[i])sum-=a[i];
else{
sum+=a[i];
break;
}
}
cout<<sum<<'\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >>t;
init();
while(t--){
solve();
}
return 0;
}
千万千万要注意移位时写 1ll 而不是1啊!防止溢出防止溢出!!(感谢雪长!www)
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
int t,ct=1;
ll a[40],n;
void init(){
a[0]=1;
while(1){
a[ct]=a[ct-1]*3;
if(a[ct]>1e18)break;
ct++;
}
}
void solve(){
cin>>n;
ll sum=0,tp=n,x=0;
for(int i=ct;i>=0;i--){
if(tp>=a[i]){
x|=(1ll<<i);
tp-=a[i];
sum+=a[i];
}
}
if(sum==n){cout<<n<<'\n';return;}
x+=1;
sum=0;
for(int i=1;i<=ct;i++){
if(x&(1ll<<i))sum+=a[i];
}
cout<<sum<<'\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >>t;
init();
while(t--){
solve();
}
return 0;
}
【唉,这道题人家分分钟过了,窝个小菜鸡折腾了好久捏qwq,不过也学到了挺多的感觉,还是要加油加油!再次拜谢雪长们~】