题意

方伯伯有一天去参加一个商场举办的游戏。商场派了一些工作人员排成一行。每个人面前有几堆石子。

说来也巧,位置在 \(i\) 的人面前的第 \(j\) 堆的石子的数量,刚好是 \(i\) 写成 \(K\) 进制后的第 \(j\) 位。现在方伯伯要玩一个游戏,商场会给方伯伯两个整数 \(L,R\)

方伯伯要把位置在 \([L, R]\) 中的每个人的石子都合并成一堆石子。每次操作,他可以选择一个人面前的两堆石子,将其中的一堆中的某些石子移动到另一堆,代价是移动的石子数量 \(\times\) 移动的距离。

商场承诺,方伯伯只要完成任务,就给他一些椰子,代价越小,给他的椰子越多。所以方伯伯很着急,想请你告诉他最少的代价是多少。例如:\(10\) 进制下的位置在 \(12312\) 的人,合并石子的最少代价为:\(1 \times 2 + 2 \times 1 + 3 \times 0 + 1 \times1 + 2 \times 2 = 9\)即把所有的石子都合并在第三堆。

对于 \(100\%\) 的数据,\(1 \le L \le R \le 10^{15}, 2 \le K \le 20\)

题解

一道很不错的数位dp

首先考虑怎么计算每个人的最少代价. 这里我们要有调整递推的思想.

我们首先计算在\(1\)点的贡献,然后转移到\(2\)的时候,加上\(2-1\)的.

虽然我们也有其他\(O(\log_{k} i)\)的时间复杂度内甚至更简洁的做法.

但是这种方法更有优化空间. 算是一个套路吧.

然后我们可以考虑用数位dp, 分别求一开始\(1\), 和后面\(i – i-1\)的贡献.

如果\(i – i-1 < 0\), 以后只会更劣.

显然这个东西是一个单峰的(中位数证明), 所以解法正确.

代码

点击查看代码
#include <stdio.h>
#include <string.h>
#include <iostream>
#define LL long long
using namespace std;

LL L, R, k, a[60], n, ans, j, f[30][2][10005];

LL dfs(int x, int sta, int sum) {
	if (x == 0) return max(0, sum);
	if (f[x][sta][sum] != -1) return f[x][sta][sum];
	f[x][sta][sum] = 0; int lim = sta ? a[x] : k - 1; LL res = 0;
	for(int i = 0; i <= lim; ++i)
		res += dfs(x - 1, sta && (i == lim), sum + (j == 1 ? (x - 1) * i : ((x >= j ? i : (-i)))));
	if (sum >= 0) f[x][sta][sum] = res;
	return res;
}

LL solve(LL val) {
	for(n = 0; val; val /= k) a[++n] = val % k; // 第n位是高位
	for(j = 1, ans = 0; j <= n; ++j) {
		memset(f, -1, sizeof(f));LL tmp = dfs(n, 1, 0);
		if (tmp < 0) continue;
		ans += (j == 1 ? 1 : -1) * tmp;
	}
	return ans;
}

int main() {
	scanf("%lld%lld%lld", &L, &R, &k);
	printf("%lld\n", solve(R) - solve(L - 1));
	return 0;
}

原文地址:http://www.cnblogs.com/absolutey/p/16906894.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性