[Luogu P3286] [BZOJ 3598] [SCOI2014]方伯伯的商場之旅

洛谷傳送門

BZOJ傳送門

題目描述

方伯伯有一天去參加一個商場舉辦的遊戲。商場派了一些工作人員排成一行。每個人面前有幾堆石子。

說來也巧,位置在 ii 的人面前的第 jj 堆的石子的數量,剛好是 ii 寫成 KK 進制後的第 jj 位。現在方伯伯要玩一個遊戲,商場會給方伯兩個整數 L,RL,R

方伯伯要把位置在 [L,R][L, R] 中的每個人的石子都合併成一堆石子。每次操作,他可以選擇一個人面前的兩堆石子,將其中的一堆中的某些石子移動到另一堆,代價是移動的石子數量 * 移動的距離。

商場承諾,方伯伯只要完成任務,就給他一些椰子,代價越小,給他的椰子越多。所以方伯伯很着急,想請你告訴他最少的代價是多少。例如:1010 進制下的位置在 1231212312 的人,合併石子的最少代價爲:12+21+30+11+22=91 * 2 + 2 * 1 + 3 * 0 + 1 * 1 + 2 * 2 = 9即把所有的石子都合併在第三堆

輸入輸出格式

輸入格式:

輸入僅有 11 行,包含 33 個用空格分隔的整數 L,R,KL,R,K,表示商場給方伯伯的 22 個整數,以及進制數

輸出格式:

輸出僅有 11 行,包含 11 個整數,表示最少的代價。

輸入輸出樣例

輸入樣例#1:

3 8 3

輸出樣例#1:

5

說明

1LR1015,2K201 \le L \le R \le 10^{15}, 2 \le K \le 20

解題分析

wtcl, 這道題都想了好久…

顯然還是數位dpdp。 注意到對於一個有kk位數的PP, 如果把所有數位的數字數都放在第mm位, 那麼貢獻是i=1kim×dgt[i]\sum_{i=1}^{k}|i-m|\times dgt[i]

那麼我們可以先把所有數放在最後一位, 然後逐位向前移。 從第ii位移到第i1i-1位的減少貢獻就是前i1i-1位的數位和減去後Ki+1K-i+1位的數位和。 如果這個值小於0, 直接returnreturn就好。

由於K20K\le 20, 所以所有數位和不會太大, 直接設dp[i][j]dp[i][j]表示前ii位數位和爲jj 的貢獻之和就好了。

代碼如下:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
int num[70];
int K;
ll l, r;
ll dp[50][2050];
ll DFS(R int dgt, R int sum, R bool lim)
{
	if (!dgt) return sum;
	if ((!lim) && (~dp[dgt][sum])) return dp[dgt][sum];
	int up = lim ? num[dgt] : K - 1;
	ll ret = 0;
	for (R int i = 0; i <= up; ++i)
	ret += DFS(dgt - 1, sum + (dgt - 1) * i, lim & (i == up));
	if (!lim) dp[dgt][sum] = ret;
	return ret;
}
ll DFS(R int dgt, R int sum, R int pos, R bool lim)
{
	if (sum < 0) return 0;
	if (!dgt) return sum;
	if ((!lim) && (~dp[dgt][sum])) return dp[dgt][sum];
	int up = lim ? num[dgt] : K - 1;
	ll ret = 0;
	for (R int i = 0; i <= up; ++i)
	if (dgt >= pos) ret += DFS(dgt - 1, sum + i, pos, lim & (i == up));
	else ret += DFS(dgt - 1, sum - i, pos, lim & (i == up));
	if (!lim) dp[dgt][sum] = ret;
	return ret;
}
ll solve(R ll val)
{
	int cnt = 0;
	std::memset(dp, -1, sizeof(dp));
	W (val) num[++cnt] = val % K, val /= K;
	ll ret = DFS(cnt, 0, 1);
	for (R int i = 2; i <= cnt; ++i)
	std::memset(dp, -1, sizeof(dp)), ret -= DFS(cnt, 0, i, 1);
	return ret;
}
int main(void)
{
	scanf("%lld%lld%d", &l, &r, &K);
	printf("%lld", solve(r) - solve(l - 1));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章