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. 2 n , n / = 2 2|n,n/=2
  2. 3 n , n / = 3 3|n,n/=3
  3. 5 n , n / = 5 5|n,n/=5
  4. n ± = 1 n\pm=1

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

再来一个性质: 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 .

然后状态复杂度大概: O ( 6 0 3 ) 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;
}


猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/106330884