AtCoder Grand Contest 044 A Pay to Win

題意: 有4種操作,你需要用最少的操作使得0變成n.

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

反過來考慮這個問題,

  1. 2n,n/=22|n,n/=2
  2. 3n,n/=33|n,n/=3
  3. 5n,n/=55|n,n/=5
  4. n±=1n\pm=1

有個顯然的性質:
最優情況下,對於n,經過若干次±\pm後再除以k(k{2,3,5})k(k\in\{2,3,5\}),得到的值yy,一定滿足y[nk,nk]y\in [\lfloor\dfrac{n}{k}\rfloor,\lceil\dfrac{n}{k}\rceil].
因爲如果y<nky<\lfloor\dfrac{n}{k}\rfloor,那麼先除再±\pm肯定更快.
對於y>nky>\lceil\dfrac{n}{k}\rceil,同理.

再來一個性質:nab=nab,nab=nab\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.

然後狀態複雜度大概:O(603)O(60^3).

暴力出奇跡!

#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章