AtCoder Grand Contest 044 A Pay to Win

题意: 有4种操作,你需要用最少的操作使得0变成n.

  1. × 2 \times2 ×2
  2. × 3 \times3 ×3
  3. × 5 \times5 ×5
  4. ± 1 \pm 1 ±1.

反过来考虑这个问题,

  1. 2 ∣ n , n / = 2 2|n,n/=2 2n,n/=2
  2. 3 ∣ n , n / = 3 3|n,n/=3 3n,n/=3
  3. 5 ∣ n , n / = 5 5|n,n/=5 5n,n/=5
  4. n ± = 1 n\pm=1 n±=1

有个显然的性质:
最优情况下,对于n,经过若干次 ± \pm ±后再除以 k ( k ∈ { 2 , 3 , 5 } ) k(k\in\{2,3,5\}) k(k{2,3,5}),得到的值 y y y,一定满足 y ∈ [ ⌊ n k ⌋ , ⌈ n k ⌉ ] y\in [\lfloor\dfrac{n}{k}\rfloor,\lceil\dfrac{n}{k}\rceil] y[kn,kn].
因为如果 y < ⌊ n k ⌋ y<\lfloor\dfrac{n}{k}\rfloor y<kn,那么先除再 ± \pm ±肯定更快.
对于 y > ⌈ n k ⌉ y>\lceil\dfrac{n}{k}\rceil y>kn,同理.

再来一个性质: ⌊ ⌊ n a ⌋ b ⌋ = ⌊ n a b ⌋ , ⌈ ⌈ n a ⌉ b ⌉ = ⌈ n a b ⌉ \lfloor\dfrac{\lfloor\frac{n}{a}\rfloor}{b}\rfloor=\lfloor\dfrac{n}{ab}\rfloor,\lceil\dfrac{\lceil\frac{n}{a}\rceil}{b}\rceil=\lceil\dfrac{n}{ab}\rceil ban=abn,ban=abn.

然后状态复杂度大概: O ( 6 0 3 ) O(60^3) O(603).

暴力出奇迹!

#include
#include
#include
#include
#define gc getchar()
using namespace std;
typedef long long ll;

template<class o> void qr(o&x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
	while(isdigit(c))x=x*10+c-'0',c=gc;
	x*=f;
}

int T;
ll n,a,b,c,d;
map<ll,ll> s;

ll f(ll n) {
	if(s.count(n)) return s[n];
	ll res=n<1e18/d?n*d:(ll)1e18;
	ll l1=(n/2)*2,r1=((n+1)/2)*2;
	ll l2=(n/3)*3,r2=((n+2)/3)*3;
	ll l3=(n/5)*5,r3=((n+4)/5)*5;
	res=min(res,(n-l1)*d+f(l1/2)+a);
	res=min(res,(r1-n)*d+f(r1/2)+a);
	res=min(res,(n-l2)*d+f(l2/3)+b);
	res=min(res,(r2-n)*d+f(r2/3)+b);
	res=min(res,(n-l3)*d+f(l3/5)+c);
	res=min(res,(r3-n)*d+f(r3/5)+c);
	return s[n]=res;
}

int main() {
	qr(T); while(T--) {
		qr(n); qr(a); qr(b); qr(c); qr(d);
		s.clear(); s[0]=0; s[1]=d; printf("%lld\n",f(n));
	}
	return 0;
}


你可能感兴趣的:(AtCoder Grand Contest 044 A Pay to Win)