Codeforces Round #595 (Div. 3)
C-Good Numbers (hard version)
easy version 就不多做赘述了,预处理也行,直接做hard version更好
题意:如果正整数可以表示为3的不同幂的总和(即不允许重复使用3的幂),则将其称为好。给你一个数n,找出大于n的最小的好数
其实这个题非常明确,先把n转换为三进制数,根据进制转换的公式可知(公式放在edu83题解里面,懒得搬过来了我是懒人)当三进制数里没有2时,就没有重复使用3的幂。题目要求找大于n的最小好数,那么我们拿到三进制数之后只需要讲所在位最高的2+1就完了。具体来说就是从最高位的2开始走,找到的第一个位0的位,使该位为1,比该位低的位全为0。
具体看代码叭
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
#define mod 1e10+7
#define sd(n) scanf("%d", &n)
#define sdd(n,m) scanf("%d%d",&n,&m)
#define sddd(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define sld(n) scanf("%lld",&n)
#define sldd(n,m) scanf("%lld%lld",&n,&m)
#define slddd(n,m,k) scanf("%lld%lld%lld",&n,&m,&k)
#define m(a,b) memset(a,b,sizeof(a))
#define rep(i, a, b) for (register int i(a); i <= (b); ++i)
#define per(i, a, b) for (register int i(a); i >= (b); --i)
//const ll maxn=;
//inline ll fpm(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
//ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
inline ll fastPow(ll a,ll b) {ll res=1; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a;a=a*a;}return res;}
//inline void rd(int &x){x=0;int f(1);char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;}
//inline void lrd(ll &x){x=0;int f(1);char ch=getchar();while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;}
//inline void wt(int x){if(x==0){putchar(48);return;}int len=0,dg[20];while(x>0){dg[++len]=x%10;x/=10;}for(int i=len;i>=1;i--)putchar(dg[i]+48);}
//inline void lwt(ll x){if(x==0){putchar(48);return;}int len=0,dg[20];while(x>0){dg[++len]=x%10;x/=10;}for(int i=len;i>=1;i--)putchar(dg[i]+48);}
//inline int MOD(int x, int y){return x - y * (x / y);} // x%y => MOD(x,y)
const int k(3);
const int maxn = 40;
int t, vis[maxn]; ll n;
void numberChange()
{
m(vis, 0);
ll recAns = n;
ll ans = 0;
register int i = 1;
int rec = 0, recZ = 0;
while (n) {
vis[i] = n % 3;
n /= 3;
if (vis[i] == 2) rec = max(rec, i);
//if (!vis[i] && i > rec) recZ = min(recZ, i);
++i;
}
for (register int j = rec; j <= i ; j++) {
if (!vis[j]) {
vis[j] = 1;
rec = j;
break;
}
}
// j=j-1
//if(recZ==0)
if (!rec) {
printf("%lld
", recAns);
}
else{
for (register int j = rec ,time=j-1; j <= i; ++j,++time) {
ans += (vis[j] * fastPow(3,time));
}
printf("%lld
", ans);
}
}
void solve()
{
numberChange();
}
int main()
{
sd(t);
while (t--) {
sld(n);
solve();
}
return 0;
}
事实上第一次交的时候这个代码连easy version都没过,TLE 了,我以为是我算法写假了,就去看了下其他人的解法,找到了另外一个更好的解法。
在小于n的数中贪心的找最大的没有重复使用3的幂的数,然后再从最低位进行遍历,最低位的0之前把低位的3的幂给减去,然后再加上0所在的i位对应的3的幂-3^i;这样就能得到大于n的最小的ans
具体看代码叭
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
#define mod 1e10+7
#define sd(n) scanf("%d", &n)
#define sdd(n,m) scanf("%d%d",&n,&m)
#define sddd(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define sld(n) scanf("%lld",&n)
#define sldd(n,m) scanf("%lld%lld",&n,&m)
#define slddd(n,m,k) scanf("%lld%lld%lld",&n,&m,&k)
#define m(a,b) memset(a,b,sizeof(a))
#define rep(i, a, b) for (register int i(a); i <= (b); ++i)
#define per(i, a, b) for (register int i(a); i >= (b); --i)
//const ll maxn=;
//inline ll fpm(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
//ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
inline ll fastPow(ll a, ll b) { ll res = 1; assert(b >= 0); for (; b; b >>= 1) { if (b & 1)res = res * a; a = a * a; }return res; }
inline void rd(int& x) { x = 0; int f(1); char ch = getchar(); while (ch < '0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }while (ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); x *= f; }
inline void lrd(ll& x) { x = 0; int f(1); char ch = getchar(); while (ch < '0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }while (ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); x *= f; }
//输出优化还没printf快,等我再研究研究
//inline void wt(int x){if(x==0){putchar(48);return;}int len=0,dg[20];while(x>0){dg[++len]=x%10;x/=10;}for(int i=len;i>=1;i--)putchar(dg[i]+48);}
//inline void lwt(ll x){if(x==0){putchar(48);return;}int len=0,dg[20];while(x>0){dg[++len]=x%10;x/=10;}for(int i=len;i>=1;i--)putchar(dg[i]+48);}
inline int MOD(int x, int y) { return x - y * (x / y); } // x%y => MOD(x,y)
const int maxn = 50;//数组开大了
int vis[maxn], t; ll n;
void solve()
{
m(vis, 0);
ll ans = 0;
for (register int i = 38; i >= 0; i--) {
register ll tmp = fastPow(3, i);
if (ans + tmp < n) {
ans += tmp;
vis[i] = 1;
}
}
for (register int i = 0; i <= 38; i++) {
if (vis[i] == 1) {
ans -= fastPow(3, i);
}
else {
ans += fastPow(3, i);
break;
}
}
printf("%lld
", ans);
}
int main()
{
sd(t);
while (t--) {
sld(n);
solve();
}
return 0;
}
事实上这个第一次交也没过,后来发现主要原因使最开始乱开的vis数组,开得太大了,memset初始化的时候被制裁了,把数组大小改小之后两个写法都能过hard version,本来我觉得第二个算法看起来更简单但是实际上第一种写法跑起来更快。