育·谜题

题目简述

$T$ 组询问,每次询问给定 $a,b,c$,你有一个初值为 $0$ 的 $x$,你现在可以进行两种操作:

$1.$ 将 $x$ 变成 $x+1$,或者将 $x$ 变成 $x - 1$,此操作会产生 $a$ 的代价。

$2.$ 将 $x$ 变成 $2x$,此操作会产生 $b$ 的代价。

对于每个询问,你需要输出将 $x$ 变成 $c$ 的最小代价。询问互相独立。

$T\le 10^4,\ c \le 10^{18},\ 0\le a,b \le 10^9$

思路

可以反过来考虑,将 $c$ 变成 $0$ 的最小代价。

首先最简单的操作方法肯定是每次减一,这样的代价是 $c×a$,我们不妨将它作为初始答案,接着考虑使用除以 2 的操作代替一些减法更新答案。

  • 如果现在的 $c$ 含有 3(这里指的是二进制下的,即 (c&3)==3 ),那么可以加一也可以减一,我们可以先加上,然后右移后决策(除了最后一步,永远不会亏)。
  • 否则如果含有 1,那么减去比加上更优。
  • 否则除以 2 一定最优,因为不会有多余的 1 需要处理。

可以发现决策事实上只会在 $c$ 每次右移后发生,单次询问复杂度为 $logc$。

其实也可以正着考虑:一次乘法之后只有一次加或减,因为如果连续地加或者减,不如在先加(或减)再乘,可以少一个 $a$。于是设 $f_x$ 表示生成 $x$ 的代价,那么有 $f_{2x}=\min(2ax,f_x+b),\ f_{2x+1}=\min{2ax,f_x+a+b,f_{x+1}+a+b}$。可以发现只有 $⌊\frac{c}{2^k}⌋,\ ⌈\frac{c}{2^k}⌉$ 是有效状态,单次询问复杂度为 $logc$。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<climits>
#include<vector>
#include<queue>
#define int long long
using namespace std;
int T;

inline int Read(){
	int num = 0, op = 1;
	char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-') op = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		num = num * 10 + ch - '0';
		ch = getchar();
	}
	return num * op;
}

signed main(){
	T = Read();
	while(T--){
		int a = Read(), b = Read(), c = Read();
		int f = c*a, cost = 0, plus = 0;
		while(c){
			f = min(f, (c-plus) * a + cost);
			plus = 0;
			printf("%lld\n", c);
			if((c&3) == 3){
				c--;
				printf("%lld\n", c);
				plus = 1;
				cost += a;
			}
			else if((c&1) == 1){
				c--;
				cost += a;
			}
			cost += b;
			c >>= 1;
		}
		printf("%lld\n", f);
	}
	return 0;
}
Sorry potatoler, but I cannot recognize you.
Built with Hugo
Theme Stack designed by Jimmy